thumbnail

子注文のフルフィルメントで親注文を自動フルフィルメントする方法|Shopify Flow


本ページでは、子注文がフルフィルメントされた時に、親注文を自動でフルフィルメントするShopify Flowの構成と運用手順をご案内します。

以下のFlowファイルをインポートするとShopify Flowを設定できます。

親注文自動フルフィルメント.flow
10.9KB


支払い確定方法を「注文全体のフルフィルメントが完了したときに自動で」に設定している場合

ポイント

標準機能では、子注文を発送しても親注文が自動的に発送済みになりません。

関連:支払い確定方法を「注文全体のフルフィルメントが完了したときに自動で」に設定している場合の注意点


親子注文で生成された親注文

関連:親子注文が生成される条件


設定後の実際の動きは以下のとおりです。

  1. 子注文が発送される(フルフィルメント作成)と、ワークフローが起動する

  2. 起動元の子注文から親注文を特定し、同じ親注文の子注文をすべて取得する

  3. すべての子注文が発送済みであれば、親注文を自動でフルフィルメントする

  4. 未発送やキャンセルなどの条件がある場合は、自動処理を行わず終了する

起動タイミングは「子注文の発送ごと」です。最後の子注文が発送された時点で、親注文がフルフィルメントされます。


#

ブロック

設定

1

トリガー

Fulfillment created

2

Run code

子注文のタグ #N:子注文 から親注文番号、注文属性 parent_shopify_order_id から親注文 ID を取得

3

Condition

親注文 ID・親注文番号が空でない

4

Get order data

id:{{runCode.parentId}} AND tag:'親注文'(最大10件)

5

Get order data

tag:'#{{runCode.parentNumber}}:子注文'(最大100件)

6

Run code

親注文が 1 件・未キャンセル、子注文にキャンセルなし、全子注文が発送済み(起動元はトリガーの値で判定)なら shouldFulfill: true。

親注文の ON_HOLD / OPEN のフルフィルメントオーダーを fos で返す

7

Condition

runCode1.shouldFulfill が true

8

For each

runCode1.fos の各アイテムで繰り返す

9

Condition

fosForeachitem.status が ON_HOLD

10

Send Admin API request(9 が True)

fulfillmentOrderReleaseHold { "id": "{{fosForeachitem.id}}" }

11

Send Admin API request(10 の後 / 9 が False)

fulfillmentCreate { "fulfillment": { "lineItemsByFulfillmentOrder": [{ "fulfillmentOrderId": "{{fosForeachitem.id}}" }], "notifyCustomer": false } }


Shopify管理画面 > アプリ > Shopify Flow に進み、ワークフローを作成 をクリック

トリガーに Fulfillment created を選択

Run codeアクションを追加し、以下の3項目の設定を行う

GRAPHQLに以下を設定

Plaintext
query {
  fulfillment {
    order {
      tags
      customAttributes { key value }
    }
  }
}

JAVASCRIPTに以下を設定

Plaintext
export default function main(input) {
  const order = input.fulfillment.order;
  let parentNumber = "";
  for (const tag of order.tags || []) {
    const m = tag.match(/^#(\d+):子注文$/);
    if (m) { parentNumber = m[1]; break; }
  }
  let parentId = "";
  for (const a of order.customAttributes || []) {
    if (a.key === "parent_shopify_order_id" && a.value) parentId = a.value.split("/").pop();
  }
  return { parentNumber, parentId };
}

SDLに以下を設定

Plaintext
type Output {
  parentNumber: String!
  parentId: String!
}

このアクションの参照名は runCode になります。

Conditionを追加し、以下を設定

項目

設定値

条件1・対象

Parent number(runCode.parentNumber)

条件1・判定

空ではなく、存在する

結合

AND

条件2・対象

Parent id(runCode.parentId)

条件2・判定

空ではなく、存在する

分岐の接続:

  • True: STEP5へ進む

  • False: 何もせず終了する

Get order data アクションを追加し、以下を設定

項目

設定値

検索クエリ

id:{{runCode.parentId}} AND tag:'親注文'

並び順

作成日時(CREATED_AT) の 昇順(ASCENDING)

最大取得件数

10

もう1つGet order dataアクションを追加し、以下を設定

項目

設定値

検索クエリ

tag:'#{{runCode.parentNumber}}:子注文'

並び順

作成日時(CREATED_AT) の 昇順(ASCENDING)

最大取得件数

100

Run codeアクションを追加し、以下の3項目の設定を行う

GRAPHQLに以下を設定

Plaintext
query {
  fulfillment { order { id displayFulfillmentStatus } }
  getOrderData { id cancelledAt fulfillmentOrders { id status } }
  getOrderData1 { id cancelledAt displayFulfillmentStatus }
}

JAVASCRIPTに以下を設定

Plaintext
export default function main(input) {
  const trigger = input.fulfillment.order;
  const parents = input.getOrderData || [];
  // 検索結果が古い可能性があるので、起動元の子注文はトリガー側の値で上書きする
  const children = (input.getOrderData1 || []).map((c) =>
    c.id === trigger.id ? Object.assign({}, c, { displayFulfillmentStatus: trigger.displayFulfillmentStatus }) : c
  );
  const stop = (reason) => ({ shouldFulfill: false, fos: [], reason });

  if (parents.length !== 1) return stop("親注文が1件に特定できない: " + parents.length);
  if (parents[0].cancelledAt) return stop("親注文がキャンセル済み");
  if (children.length >= 100) return stop("子注文が100件以上");
  if (!children.some((c) => c.id === trigger.id)) return stop("起動元の子注文が検索結果に無い");
  if (children.some((c) => c.cancelledAt)) return stop("キャンセル済みの子注文あり: 手動で金額を調整して確定");
  if (!children.every((c) => c.displayFulfillmentStatus === "FULFILLED")) return stop("未発送の子注文が残っている");

  const fos = parents[0].fulfillmentOrders || [];
  return {
    shouldFulfill: true,
    fos: fos
      .filter((f) => f.status === "ON_HOLD" || f.status === "OPEN")
      .map((f) => ({ id: f.id, status: f.status })),
    reason: "ok: " + fos.map((f) => f.status).join(","),
  };
}

SDLに以下を設定

Plaintext
type Fo {
  id: ID!
  status: String!
}

type Output {
  shouldFulfill: Boolean!
  fos: [Fo!]!
  reason: String!
}

判定条件(すべて満たすと shouldFulfill: true):

  • 親注文が1件に特定できる

  • 親注文がキャンセルされていない

  • 子注文が100件未満である

  • 起動元の子注文が検索結果に含まれる

  • キャンセル済みの子注文がない

  • すべての子注文が発送済み(FULFILLED)である

このアクションの参照名は runCode1 になります。

Conditionを追加し、判定がtrueの場合のみフルフィルメント実行に進む

項目

設定値

条件・対象

Run code 1 should fulfill(runCode1.shouldFulfill)

条件・判定

等しい true

分岐の接続:

  • True: STEP9へ進む

  • False: 何もせず終了する

For eachを追加、判定で対象となったフルフィルメントオーダーを1件ずつ処理する

項目

設定値

繰り返す対象(リスト)

runCode1.fos

繰り返しの中にConditionを追加し、フルフィルメントオーダーが保留中(ON_HOLD)かどうかで処理を分ける

項目

設定値

条件・対象

Fos foreach item status(fosForeachitem.status)

条件・判定

等しい ON_HOLD

分岐の接続:

  • True: STEP11 → STEP12の順に実行する

  • False: STEP13を実行する

Trueの分岐にSend Admin API requestを追加し、保留を解除する

項目

設定値

操作(API)

fulfillmentOrderReleaseHold

リクエスト内容:

Plaintext
{ "id": "{{fosForeachitem.id}}" }

STEP11の直後にSend Admin API requestを追加し、発送を確定する

項目

設定値

操作(API)

fulfillmentCreate

リクエスト内容:

Plaintext
{
  "fulfillment": {
    "lineItemsByFulfillmentOrder": [{ "fulfillmentOrderId": "{{fosForeachitem.id}}" }],
    "notifyCustomer": false
  }
}

STEP10のFalse分岐にSend Admin API requestを追加し、保留がないフルフィルメントオーダーをそのまま発送確定する

項目

設定値

操作(API)

fulfillmentCreate

リクエスト内容:

Plaintext
{
  "fulfillment": {
    "lineItemsByFulfillmentOrder": [{ "fulfillmentOrderId": "{{fosForeachitem.id}}" }],
    "notifyCustomer": false
  }
}

ポイント

STEP12・13はどちらも発送通知を送りません(notifyCustomer: false)

子注文の発送時に通知する運用を想定しています。


  1. 子注文をフルフィルメントする(例:#1001:子注文 のタグが付いた子注文)

    • 子注文が複数ある場合は、すべての子注文をフルフィルメントする

  2. 対応する親注文(例:#1001)が発送済みになることを確認する

  3. Flowの実行ログで、各ステップが正常に通過しているか確認する