TECH JOURNAL

Shopify Fulfillment API で複数拠点の出荷を自動化する — 在庫割り当てから追跡番号登録まで

ShareXB!
Shopify Fulfillment API で複数拠点の出荷を自動化する — 在庫割り当てから追跡番号登録まで
目次

はじめに

Shopify で複数の倉庫(Location)を持ち、注文ごとに最適な拠点から出荷したい場合、Fulfillment API を正しく使いこなす必要がある。2023 年に Shopify が REST ベースの旧 Fulfillment API から FulfillmentOrder ベースの新 API に移行を完了させたが、いまだ旧 API を使い続けているシステムも多い。ここでは新 API に合わせた完全な実装を解説する。

旧 API と新 API の違い

項目旧 API (deprecated)新 API
リソースOrder > LineItemFulfillmentOrder > FulfillmentOrderLineItem
在庫割り当て手動Location Assignment で自動
部分出荷難しいFulfillmentOrder を分割して対応

旧 API でできていた「Order ID を直接指定して出荷」は新 API では不可。必ず FulfillmentOrder ID を経由する。

実装フロー

1. Order 確定イベント受信(Webhook: orders/create)
2. Order に紐づく FulfillmentOrder 一覧を取得
3. FulfillmentOrder に在庫割り当て済み Location を確認
4. 出荷処理(WMS への連携)
5. 追跡番号が確定したら FulfillmentOrder に登録
6. Shopify が顧客に発送通知メールを自動送信

Step 1: FulfillmentOrder の取得

const query = `
  query getFulfillmentOrders($orderId: ID!) {
    order(id: $orderId) {
      fulfillmentOrders(first: 10) {
        nodes {
          id
          status
          assignedLocation {
            location {
              id
              name
            }
          }
          lineItems(first: 50) {
            nodes {
              id
              remainingQuantity
              variant {
                id
                sku
              }
            }
          }
        }
      }
    }
  }
`
const { data } = await shopify.graphql(query, {
  orderId: `gid://shopify/Order/${orderId}`,
})
const fulfillmentOrders = data.order.fulfillmentOrders.nodes

Step 2: 出荷可能な FulfillmentOrder を特定する

status: OPEN かつ remainingQuantity > 0 の FulfillmentOrder のみが出荷対象。部分出荷の場合、同一 Order が複数の FulfillmentOrder に分かれることがある。

const openFulfillmentOrders = fulfillmentOrders.filter(
  (fo) =>
    fo.status === "OPEN" &&
    fo.lineItems.nodes.some((li) => li.remainingQuantity > 0)
)

Step 3: 追跡番号登録

WMS から追跡番号を受け取ったら fulfillmentCreate ミューテーションで登録する。

const mutation = `
  mutation createFulfillment($fulfillment: FulfillmentInput!) {
    fulfillmentCreate(fulfillment: $fulfillment) {
      fulfillment {
        id
        status
        trackingInfo {
          number
          url
        }
      }
      userErrors {
        field
        message
      }
    }
  }
`
await shopify.graphql(mutation, {
  fulfillment: {
    lineItemsByFulfillmentOrder: [
      {
        fulfillmentOrderId: fulfillmentOrder.id,
        fulfillmentOrderLineItems: fulfillmentOrder.lineItems.nodes.map((li) => ({
          id: li.id,
          quantity: li.remainingQuantity,
        })),
      },
    ],
    trackingInfo: {
      company: "ヤマト運輸",
      number: trackingNumber,
      url: `https://jizen.kuronekoyamato.co.jp/jizen/servlet/crjz.b.NQ0010?id=${trackingNumber}`,
    },
    notifyCustomer: true, // true にすると Shopify が発送通知メールを自動送信
  },
})

複数拠点での在庫割り当て自動化

複数 Location がある場合、Shopify はストアの Location Priority 設定に基づいて自動割り当てを行う。ただし在庫不足の Location に割り当てられた場合、move ミューテーションで別 Location に移し替えることができる。

// FulfillmentOrder を別 Location に移動
const moveMutation = `
  mutation moveFulfillmentOrder($id: ID!, $newLocationId: ID!) {
    fulfillmentOrderMove(id: $id, newLocationId: $newLocationId) {
      movedFulfillmentOrder { id assignedLocation { location { name } } }
      userErrors { field message }
    }
  }
`

エラーハンドリング

userErrors は HTTP 200 で返ってくることに注意。GraphQL のエラーとは別物で、ビジネスロジックエラー(在庫不足・ステータス不正など)はここに入る。

const { userErrors } = result.fulfillmentCreate
if (userErrors.length > 0) {
  throw new Error(`Fulfillment failed: ${userErrors.map((e) => e.message).join(", ")}`)
}

まとめ

Shopify の新 Fulfillment API は旧 API より設計が洗練されているが、FulfillmentOrder の概念を理解するまで複雑に感じる。一度理解すると、部分出荷・複数拠点・追跡番号登録の組み合わせが非常に扱いやすくなる。

ShareXB!

この記事を書いた人

渡部 誠也

執行役員 / CTO

独立系 SIer で Web・組み込み・基幹システムの開発を経験し、2017 年に illustrious へ。CTO としてシステム開発事業を立ち上げ、要件定義からコーディングまで一貫して担う。EC に特化した Web アプリケーションを数多く手がける。

ECの業務やシステムについて、
ご相談ください。

いまの運用で困っていること、実現したいことから、一緒に整理します。