Re: How to migrate to fulfillment orders as a middleman

How to migrate to fulfillment orders as a middleman

aveshopstech
Shopify Partner
33 1 28

I've spent hours reading the migration guide, docs, forum posts, and more, and I'm hardly closer to understanding how to properly migrate to fulfillment orders for API v2022-07 than when I started. We don't seem to fully fit any of the models described, and much of the information is unclear or lacking in enough detail. I'm sincerely hoping that someone can provide some specific guidance for our scenarios.

 

Overview

Our system sits in between a "wholesale" Shopify store and many client Shopify stores. It gets inventory values from the wholesale store and updates client Shopify stores. It also get orders from the client Shopify stores and creates analogous orders on the wholesale store for fulfillment. When an order is fulfilled on the wholesale store, the system pushes the fulfillment info (tracking, etc.) back to the source client store and marks the appropriate order line items as fulfilled. All this is accomplished with a custom/private app between our system and the wholesale store, and a published (unlisted) app that is installed by all client stores.

 

Workflow 1 - Receive & Store Wholesale Fulfillment Info

Step one in the process is to receive and persist wholesale fulfillment info, which will then be pushed in the next workflow to the analogous order on the client store that placed the order. Our wholesale store private app has registered the fulfillments/create webhook. When an order's line items are fulfilled, we get the webhook payload and save it in our database. The structure of this data is a `fulfillments` table and a `fulfillment_line_items` table with foreign key linking it to the `fulfillments` table. These two tables essentially represent the Shopify Fulfillments resource from the perspective of the wholesale store, where the `fulfillments` table primarily stores the shopify fulfillment ID and tracking info, and the `fulfillment_line_items` table stores the internal ID for the line item and the quantity that was fulfilled.

 

Migration Questions:

  1. Can (should) we continue to use the fulfillments/create webhook to get fulfillment notifications from wholesale? Even the latest API version still includes this webhook topic, so it doesn't appear to be deprecated.
  2. Since we don't directly manipulate fulfillments on the wholesale store (just receive/read data), is there any reason we'd need to get the FulfillmentOrder ID and/or other FulfillmentOrder information for the each wholesale order Fulfillment?
    1. Correct me if I'm wrong, but neither the fulfillments/create payload nor the Fulfillments resource via the REST API include the ID of the parent FulfillmentOrder resource. It seems like if that's needed we'd have to do a GraphQL query to get FulfillmentOrder info for the related Order whenever a Fulfilment was created.
  3. For one-off purposes (such as reconciling fulfillment info), we sometimes also use the REST API `[GET] /orders/{order_id}/fulfillments` endpoint to get all fulfillment data for a particular Order. This doesn't seem to be deprecated as of 2023-04. Is there any reason we shouldn't continue to use this?
  4. Generally, how does our current process for this first workflow need to be modified, if at all, to support the new FulfillmentOrders paradigm?


Workflow 2 - Send Fulfillment Info to Client Stores

With the wholesale order fulfillment information stored, we now need to push the fulfillment data back to the store that originally placed the order. This is done via the REST API's `[POST] /orders/{order_id}/fulfillments` endpoint. We then take the response data and save it to our database. The structure of this data is essentially the same as wholesale order fulfillments, except that there's a different `shop_fulfillments` table for the main Fulfillment record. Since the wholesale order and client store orders are analogous, at least for our purposes, the original `fulfillment_line_items` table from workflow 1 also includes a foreign key to this `shop_fulfillments` table. So, these two tables together represent a Fulfillment resource, but from the perspective of the client store. All of this info is saved internally because our client stores may log into our system directly to perform various actions and view data, such as their orders and order fulfillment statuses.

 

Migration Questions:

  1. The `[POST] orders/{order_id}/fulfillments` endpoint has been deprecated. It appears that the `[POST] /fulfillments` endpoint is the closest replacement for this action. Is this the best method?
  2. The `[POST] /fulfillments` endpoint requires passing fulfilled line items by FulfillmentOrderID. At what point is a FulfillmentOrder created? Who creates it and how?
  3. Depending on the answer to the 2nd question, which one of the following two methods is correct? (Or is there an entirely different process that should be followed?)
    1. In order to accomplish the same goal of setting fulfillment info on an order (by line items), it appears that we'd have to first query to get all of the FulfillmentOrders for a give client store order, filter by line items to make sure we have the correct FulfillmentOrder(s), then use those IDs to post a fulfillment (per question 2) to the correct FulfillmentOrder resource(s).
    2. -OR- Do we create the FulfillmentOrder first, THEN post a fulfillment to it?
  4. Generally, and if my questions are not on the right track, how does our current process for this second workflow need to be modified to support the new FulfillmentOrders paradigm?


Conclusion & Final Questions

I'm not even sure if I'm asking the right questions because there is still gap in my understanding of FulfillmentOrders and what aspects of fulfillments are being replaced by fulfillment orders. There are other aspects of the migration that are discussed in various documents and articles that I'm not even sure how or if they apply. My main, remaining questions/concerns are:

 

  1. Future Proofing: I see that starting in API v2023-01 there are a bunch of fulfillment_orders/* webhook topics. Will it be necessary to transition to those (instead of continuing to use the fulfillments/create topic) in the future? And, if so, should I do something differently in my 2022-07 migration to prepare for that now?
    1. In regard to the fulfillment_orders/* webhook topics, I think I understand that this would provide us with more granular eventing information for fulfillments, but I'm struggling to see a single fulfillment_orders webhook topic that serves the same purpose as the fulfillments/create (with status of 'success') webhook. Which fulfillment_orders/* topic says "this was fulfilled"? Or are these meant to be used in conjunction with, and not a replacement for, the fulfillments/create topic?
  2. Permissions: There seems to be several different permissions that may or may not be needed depending on what "type of app" you are. I'm not sure that we fit exactly into any of these "types" of apps. There's [read/write]_merchant_managed_fulfillment_orders, [read/write]_third_party_fulfillment_orders, and [read/write]_assigned_fulfillment_orders. My guess is that we need [read/write]_merchange_managed_fulfillment_orders, but maybe assigned_fulfillment_orders works too? 
    1. Note: Unfortunately, we currently only have access scope permissions relating to products, orders, locations, and inventory -- no [read/write]_fulfillments. However, up to this point, we've still been able use endpoints related to fulfillment. After some testing, and per docs, we can't request granular access scopes for [read/write]_assigned_fulfillment_orders permissions without the [read/write]_fulfillments permission. However, we CAN request granular access scopes for the [read/write]_merchant_managed_fulfillment_orders permissions. Hopefully that's what we need!
  3. Fulfillment Service: I read in the migration guide (and other places) that you need to "opt in to fulfillment orders as a fulfillment service," but its very unclear if this is a general requirement, or only a requirement if your app was registered as a fulfillment service, which ours isn't. If its a general requirement, then my workflow 1 will need to change and I'll somehow need to register a callback URL. Before I dive down this rabbit hole, since we're not registered as a fulfillment service do I need to worry about this? Or is step 3 (and step 4) in the migration guide only for apps that registered as a fulfillment service? (In which case, this should really be made more clear.)

 

A huge thank you to anyone who is willing to answer any of my questions. Or, if skipping the bulk of my questions and just cutting to the chase based on my described workflows and goals makes more sense, that's great too. Either way, drinks are on me. 

Replies 5 (5)

aveshopstech
Shopify Partner
33 1 28

I've found more info on my permissions question, and I think my original assumption is correct. FWIW, and for anyone who also had questions about fulfillment orders permissions, the Shopify API access scopes documentation page provides a bit more information that helped me better understand these.  The options, according tot he migration guide, include:

  • [read/write]_merchant_managed_fulfillment_orders
    • Needed when managing FulfillmentOrder resources assigned to merchant-managed locations. (I Think this is my scenario.)
  • [read/write]_third_party_fulfillment_orders
    • Needed whtn managing FulfillmentOrder resources assigned to a location managed by any fulfillment service. Meaning, if the fulfillment service isn't yours, but the location is managed by a fulfillment service, then you'd need this.
  • [read/write]_assigned_fulfillment_orders
    • Needed for access to FulfillmentOrder resources assigned to a location managed by your fulfillment service. In other words, I think this is only required if your app is registered as a fulfillment service on merchant stores.

So, assuming your app is like mine and has NOT registered a fulfillment service on merchant stores, and you're just managing fulfillment orders created for items in a merchant-managed location, then I think that the [read/write]_merchant_managed_fulfillment_orders is all you need. If your app is managing all fulfillments on a merchant store, then you likely need *_merchant_managed_fulfillment_orders AND *_third_party_fulfillment_orders. And, finally, if your app is registered as a fulfillment service, then you'd need the *_assigned_fulfillment_orders permission to manage fulfillment orders in your own fulfillment service's location.

aveshopstech
Shopify Partner
33 1 28

After scouring additional sources of information and a bunch of trial and error, I think I can now answer most of my own questions. I'm posting in case someone can correct me, and/or for reference by others who may have similar questions. Referring back to my original post:

 

Workflow 1

  1. Still use the fulfillments/create webhook?
    1. Yes -- Fulfillments are not being replaced by FulfillmentOrders. They are simply now "contained" by and managed through FulfillmentOrders. At the end of the day, the Fulfillment resource will still have the necessary tracking information.
  2. Do we need the fulfillment order ID to store in the db for the wholesale workflow?
    1. Since we're only reading and not managing fulfillments on the wholesale store, there's probably no reason to get and store that info.
  3. Can we still use the REST API `[GET] /orders/{order_id}/fulfillments` endpoint to get all fulfillment data for a particular Order?
    1. Yes
  4. Does the current process need to be modified to support the new FulfillmentOrders paradigm?
    1. Not that I can tell.

 

Workflow 2

  1. What endpoint to use to create fulfillments?
    1. The `[POST] /fulfillments` endpoint is probably the closest replacement for the `[POST] orders/{id}/fulfillment` endpoint.
  2. How/When are OrderFulfillments created?
    1. According to the docs, they are created automatically when an order is created.
  3. How to mark order line items as fulfilled?
    1. First query to get all of the FulfillmentOrders for a give client store order, filter by line items to make sure we have the correct FulfillmentOrder(s), then use those IDs to post a fulfillment (via endpoint per question/answer 1).
  4. How does our current process for this second workflow need to be modified to support the new FulfillmentOrders paradigm?
    1. Instead of posting fulfillment data directly to an order, first lookup the FulfillmentOrder (per question/answer 3), then post the fulfillment to the FulfillmentOrder along with order line items.
    2. Store the queried fulfillment_order_id in the database for use in additional/future operations.

 

Final Questions

  1. Future Proofing: Still not sure.
  2. Permissions: See my first reply re: permissions.
  3. Fulfillment Service: Despite how the migration docs make it sound, registering as a fulfillment service is not required, so sections 3 and 4 can be ignored. (If you are registered as a fulfillment service, then you would need to follow those sections, however.)

 

Hopefully this puts me on the right track! Corrections and/or additional information are welcome and appreciated.

Michael_AG
Shopify Staff
47 8 11

Hi,

 

Thank you for your questions and suggestions. Your guesses are correct. I will answer all your questions in detail soon. Just one clarification question first.

 

Do I understand your case correctly that client stores do not own inventory, and when the customer places an order, the wholesale store is asked to fulfill and ship this order directly to the customer? The client store is more like a storefront for placing orders. Does the backward integration happen only to update the order status and shipping information for the customer and the store owner?

 

Also, a reworked FulfillmentOrder resource documentation will be released in a few days, and it will cover about the half of your questions.

To learn more visit the Shopify Help Center or the Community Blog.

aveshopstech
Shopify Partner
33 1 28

Hi @Michael_AG,

 


Do I understand your case correctly that client stores do not own inventory, and when the customer places an order, the wholesale store is asked to fulfill and ship this order directly to the customer? The client store is more like a storefront for placing orders.


Yes, that is correct. When an order is placed by a customer on the merchant's store, our app receives the order and "passes" it to (by creating a new, corresponding order on) the wholesale store. Then, the order is fulfilled/shipped via the wholesale store directly to the customer. 


Does the backward integration happen only to update the order status and shipping information for the customer and the store owner?

Exactly. The fulfillment information is passed from wholesale back to the app, and then the app marks the corresponding order items on the merchant's store as fulfilled. So, essentially, our app is the middle man and is storing (and creating in the case of the merchant) both the wholesale fulfillment resource info and the merchant shop's fulfillment resource info.

I've completed implementation of the new FulfillmentOrders paradigm based on my findings and assumptions listed in this thread and will be deploying this weekend. Thanks for confirming that my final understanding was correct. I look forward to seeing updated docs on this topic.

 

 

 

Michael_AG
Shopify Staff
47 8 11

 

> Fulfillment Service

 

As you do not create a fulfillment service entity for every shop your app work with, your app is not a fulfillment service. In terms of permissions and interactions with client shops, you are an order management app. You manage orders from merchant-managed locations, take care of them being fulfilled, and close them. So you do not need assigned_fulfillment_orders access scope. I see you have managed to get merchant_managed_fulfillment_orders access scope with request_granular_access_scopes based on the read/write_orders access scope you have had. That's it. I'll think about how we can improve steps 1-2 in the migration guide to make it more clear.

 

Your app could be a fulfillment service app and offer the same service. Merchants communicate with fulfillment service apps via fulfillment and cancellation requests, and the fulfillment service app has an option to reject requests.

 

> Can (should) we continue to use the fulfillments/create webhook to get fulfillment notifications from wholesale? Even the latest API version still includes this webhook topic, so it doesn't appear to be deprecated.

 

That's right. The Fulfillment resource and fulfillment webhooks remain. When some or all of order line items are fulfilled, you will receive `fulfillments/create` webhook.

 

> The `[POST] orders/{order_id}/fulfillments` endpoint has been deprecated. It appears that the `[POST] /fulfillments` endpoint is the closest replacement for this action. Is this the best method?

 

Right. FulfillmentOrder entity stays in the middle of orders and fulfillments, and now we granularly fulfill fulfillment orders rather then orders directly.

 

> The `[POST] /fulfillments` endpoint requires passing fulfilled line items by FulfillmentOrderID. At what point is a FulfillmentOrder created? Who creates it and how?

 

Shopify creates fulfillment orders automatically when an order is created. It is not possible to manually create fulfillment orders.

 

> Correct me if I'm wrong, but neither the fulfillments/create payload nor the Fulfillments resource via the REST API include the ID of the parent FulfillmentOrder resource.

 

Fulfillment entity does not contain fulfillment_order_id. The thing here is, the relationship between Fulfillments and FulfillmentOrders on the same order is many-to-many. One FulfillmentOrder may be fulfilled via several partial Fulfillments. One Fulfillment can contain line items from several fulfillment orders on the same order. See 

FulfillmentV2Input.lineItemsByFulfillmentOrder (same for the REST api).

 

So the question you ask is "How do I map line items from the client store order to the items fulfilled by the wholesale store".

 

1. You receive the "fulfillments/create" webhook from the wholesale site. The webhook gives you the fulfillment_id and order_id

2. You map this order_id to the client's order_id (you keep this mapping in your DB). Your query  GET /order/<client_shop_order_id>/fulfillment_orders on the client shop.

3. If there is only one FulfillmentOrder in the client's shop on this order and in the wholesale store has fulfilled the entire order at once, you can fulfill the whole order on the client site with POST /fulfillments

{
"fulfillment": {
"line_items_by_fulfillment_order": [
{
"fulfillment_order_id": <client_site_fulfillment_order_id>
}
]
}
}

 

 

To learn more visit the Shopify Help Center or the Community Blog.