mcpField
M
multi-warehouse

multi-warehouse

github.com/finsilabsその他60106 回インストール5 日前に更新
リポジトリ

multi-warehouse について

multi-warehouse は、複数の倉庫、店舗、3PLを運営する事業者向けの在庫管理スキルです。Shopify、WooCommerce、BigCommerce、カスタムストアでの注文割り当て、在庫移動、分割出荷を説明します。

主な機能

  1. 01各プラットフォームの複数拠点在庫を比較
  2. 02在庫と注文の割り当てを設定
  3. 03拠点間で在庫を移動
  4. 04分割出荷への対応と在庫の再調整

活用シーン

  1. 01倉庫までの距離やコストで注文を振り分け
  2. 02複数拠点間で在庫を再配分
  3. 03複数倉庫から注文を分割して出荷

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/finsilabs/awesome-ecommerce-skills --skill multi-warehouse

インストール後、Claude Code で/multi-warehouseを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

tile.json
evals/proximity-based-allocation-algorithm/
criteria.json
task.md
evals/split-fulfillment-records-and-customer-n/
criteria.json
task.md
evals/transfer-order-lifecycle-management/
criteria.json
task.md

詳細

複数倉庫の在庫管理

概要

複数倉庫の在庫管理では、倉庫、店舗、3PL など複数の実店舗に商品を在庫し、各注文を最も適切に出荷できる拠点へ割り当てます。Shopify と BigCommerce には、複数拠点の在庫管理機能が組み込まれています。WooCommerce ではプラグインが必要です。ゾーンベース、コスト最適化、分割出荷などのルーティングロジックがプラットフォームのツールで対応できる範囲を超える場合に限り、カスタムの割り当てエンジンを構築してください。

このスキルを使用する場面

  • 販売事業者が複数の倉庫、店舗、または 3PL(サードパーティロジスティクス)プロバイダーを運営している場合
  • 配送料が大きな要素となり、最寄りの倉庫から注文を出荷することが重要な場合
  • 拠点間の在庫の偏りを解消するため、在庫を再配分する移動注文が必要な場合
  • 1 つの拠点にすべての商品が揃っていないため、2 つの倉庫に注文を分割する必要がある場合

主な手順

ステップ 1:プラットフォームを確認し、適切なツールを選ぶ
プラットフォーム複数拠点の在庫管理機能(標準搭載)推奨拡張機能
Shopifyはい — 最大 1,000 拠点(Basic:4、Shopify プラン:5、Advanced:8)3PL の出荷ルーティングには ShipBob または Flexport、高度なルーティングルールには ShipHero
WooCommerceいいえ — プラグインが必要ATUM Multi-Inventory(アドオン)または Iconic の WooCommerce Multi-Location Inventory
BigCommerceはい — Multi-Location Inventory アプリ(無料)高度なルーティングには ShipStation、3PL 連携には Whiplash または ShipBob
カスタム/ヘッドレス近接性を優先するルーティングを備えた割り当てエンジンを構築ルーティングや分割出荷の要件を満たすプラットフォームツールがない場合に必要

ステップ 2:プラットフォーム固有の設定

Shopify

Shopify には複数拠点の在庫管理機能が標準搭載されています。

拠点を設定する:

  1. Settings → Locations → Add location に移動します
  2. 各倉庫、店舗、またはフルフィルメントセンターを追加します
  3. 住所を入力します(配送料の計算に使用されます)
  4. 各拠点に商品を割り当てます — 商品を開き、バリエーションをクリックして、各拠点の数量を設定します

出荷ルーティングを設定する:

  1. Settings → Shipping and delivery → Shipping に移動します
  2. Fulfillment from で、拠点の優先順位を設定します
  3. Shopify は、すべての商品に在庫がある拠点のうち、優先順位リストの最初にある拠点へ注文を割り当てます
  4. より高度なルーティング(近接性ベース、コスト最適化)には、App Store から ShipHero または ShipBob をインストールします

拠点間で移動注文を作成する:

  1. Inventory → Transfers → Create transfer に移動します
  2. Origin location(在庫の移動元)を選択します
  3. Destination location(移動先の拠点)を選択します
  4. 移動する商品と数量を追加します
  5. 商品が到着したら、Transfers → [Transfer] → Accept items に移動します — Shopify が移動先の在庫を自動的に増やし、移動元の在庫を減らします

3PL 連携:

  • ShipBob の場合:App Store から ShipBob をインストールします。ShipBob は Shopify から注文を取得し、最寄りのフルフィルメントセンターへ自動的に割り当てます
  • Flexport の場合:Flexport アプリをインストールします。商品と在庫が双方向に同期されます

WooCommerce

WooCommerce で複数拠点の在庫を管理するには、プラグインが必要です。

オプション A:ATUM Multi-Inventory(推奨)

  1. ATUM Inventory Management for WooCommerce(無料のコア機能)と ATUM Multi-Inventory アドオン(有料)をインストールします
  2. ATUM で Multi-Inventory → Locations → Add Location に移動します
  3. 各商品で ATUM Multi-Inventory タブに移動し、拠点ごとに数量を割り当てます
  4. 在庫の選択ルール(地域ベース、優先順位ベース、または在庫がある最初の拠点)を設定します

オプション B:Iconic の WooCommerce Multi-Location Inventory

  1. プラグインをインストールします
  2. WooCommerce → Multi-Location Inventory → Locations に移動し、倉庫を追加します
  3. 各商品を編集し、拠点ごとの在庫数量を設定します
  4. プラグイン設定でフルフィルメントのルーティングを設定します

移動注文:

  • ATUM Multi-Inventory には移動注文機能が含まれています。ATUM → Inventory Transfers → New Transfer に移動します
  • 移動元、移動先、商品、数量を設定します
  • 入庫済みとしてマークすると、在庫が自動的に更新されます

BigCommerce

BigCommerce では、無料アプリを使って複数拠点の在庫を管理できます。

複数拠点の在庫を有効にする:

  1. Apps → Search "Multi-Location Inventory" に移動します
  2. アプリをインストールして起動します
  3. 拠点を追加します。Locations → Add location で名前と住所を入力します
  4. 商品ごと、拠点ごとに在庫を割り当てます

フルフィルメントのルーティングを設定する:

  1. Multi-Location Inventory アプリで Routing Rules に移動します
  2. ルールを設定します。最寄りの拠点から発送、最も安い拠点から発送、在庫が最も多い拠点から発送などを選択できます
  3. 注文を1つの拠点から発送できない場合、BigCommerce は自動的に分割配送を生成します

3PL と連携する場合:

  • BigCommerce App Marketplace から ShipStation をインストールします
  • ShipStation は 3PL プロバイダーに接続し、ルールに基づいてルーティングします

カスタム/ヘッドレス

ヘッドレスストアフロントでは、各注文に最適な拠点を選択する割り当てエンジンを構築します。

// lib/allocation.ts
import { haversineDistance } from './geo';

interface OrderItem { variantId: string; quantity: number; productName: string; }
interface AllocationResult {
  type: 'single' | 'split';
  fulfillments: { locationId: string; items: OrderItem[] }[];
}

export async function allocateOrder(orderItems: OrderItem[], shippingAddress: Address): Promise<AllocationResult> {
  const locations = await db.locations.findMany({ where: { active: true } });

  // Fetch inventory availability for all required variants at all locations
  const inventory = await db.inventoryLevels.findMany({
    where: { variantId: { in: orderItems.map(i => i.variantId) } },
  });
  const invMap = buildInventoryMap(inventory); // locationId -> variantId -> available

  // Score locations by distance to shipping address (proximity-first routing)
  const scored = locations
    .map(loc => ({
      ...loc,
      distanceKm: haversineDistance({ lat: loc.lat, lng: loc.lng }, { lat: shippingAddress.lat, lng: shippingAddress.lng }),
    }))
    .sort((a, b) => a.distanceKm - b.distanceKm);

  // Prefer single-location fulfillment to avoid split shipments
  for (const location of scored) {
    const canFulfillAll = orderItems.every(item => (invMap[location.id]?.[item.variantId] ?? 0) >= item.quantity);
    if (canFulfillAll) return { type: 'single', fulfillments: [{ locationId: location.id, items: orderItems }] };
  }

  // Fall back to split fulfillment — greedy assignment to nearest location with stock
  const remaining = [...orderItems];
  const fulfillments: AllocationResult['fulfillments'] = [];

  for (const location of scored) {
    const canFulfill = remaining.filter(item => (invMap[location.id]?.[item.variantId] ?? 0) >= item.quantity);
    if (canFulfill.length > 0) {
      fulfillments.push({ locationId: location.id, items: canFulfill });
      canFulfill.forEach(item => remaining.splice(remaining.findIndex(r => r.variantId === item.variantId), 1));
    }
    if (remaining.length === 0) break;
  }

  if (remaining.length > 0) throw new Error('Cannot fulfill order — insufficient stock across all locations');
  return { type: 'split', fulfillments };
}

// Transfer order management
export async function receiveTransferOrder(transferOrderId: string, receivedItems: { variantId: string; quantity: number }[]) {
  const transfer = await db.transferOrders.findUnique({ where: { id: transferOrderId }, include: { items: true } });

  await db.$transaction([
    // Decrease on_hand + reserved at source
    ...receivedItems.map(item => db.inventoryLevels.update({
      where: { variantId_locationId: { variantId: item.variantId, locationId: transfer.fromLocationId } },
      data: { onHand: { decrement: item.quantity }, reserved: { decrement: item.quantity } },
    })),
    // Increase on_hand at destination
    ...receivedItems.map(item => db.inventoryLevels.upsert({
      where: { variantId_locationId: { variantId: item.variantId, locationId: transfer.toLocationId } },
      create: { variantId: item.variantId, locationId: transfer.toLocationId, onHand: item.quantity, reserved: 0 },
      update: { onHand: { increment: item.quantity } },
    })),
    db.transferOrders.update({ where: { id: transferOrderId }, data: { status: 'received' } }),
  ]);
}

ステップ 3:分割配送に関する通知を設定する

注文が2つの拠点に分割される場合、注文確定前に顧客へ知らせる必要があります。

Shopify: 分割が必要な場合、Shopify の標準チェックアウトでは配送オプションに「複数の拠点から発送」と表示されます。メッセージは Settings → Checkout → Checkout language でカスタマイズできます。

WooCommerce: ATUM が分割が必要だと検出した場合、カート/チェックアウトに通知を表示します。ATUM Multi-Inventory には、分割配送に関するメッセージを設定する機能が含まれています。

注文確認メール: すべてのフルフィルメントグループと、それぞれの発送予定日を記載します。「ご注文の商品は2回に分けて届きます」のように商品ごとの内訳を記載すると、サポートへの問い合わせを減らせます。


ステップ 4:拠点間の在庫バランスを調整する

慢性的な在庫の偏り(一方の倉庫では過剰在庫、もう一方では在庫切れ)は、移動コストや分割フルフィルメントを増加させます。週1回、分析を実施します。

在庫の偏りを示す兆候:

  • ある拠点では同じ SKU が常にゼロで、別の拠点では余剰になっている
  • 同じ SKU について、注文の20%を超える割合で分割フルフィルメントが必要になる
  • 同じ商品を同じ方向に移動する依頼が毎週繰り返される

対応: 次回の再発注サイクル前に、在庫過剰の拠点から在庫不足の拠点へ移動注文を作成します。

Shopify Stocky: 拠点間で在庫に偏りがある SKU を特定する「Inventory distribution」レポートを表示します。

ベストプラクティス

  • 可能な限り1か所から出荷する — 分割出荷はコストと混乱を増やすため、分割する前に1か所で対応できる方法を検討してください
  • アプリを購入する前に、プラットフォーム標準の複数拠点機能を使う — Shopify と BigCommerce は一般的なケースに十分対応できます。プラットフォームの機能を超える複雑な振り分けロジックが必要な場合にのみ、アプリを追加してください
  • 移動オーダーは輸送中の在庫として扱う — 移動の出荷時に移動元の引当可能在庫を減らし、商品が到着して数量を確認するまでは移動先の在庫を増やさないでください
  • 注文後ではなく、チェックアウト時に分割出荷を顧客に知らせる — 支払い後に知らされると、だまされたように感じられます
  • 毎週、在庫の偏りを分析する — 15分間の配分レポート確認で、時間とともに深刻化する慢性的な偏りを防げます

よくある落とし穴

問題解決策
分割フルフィルメントによって送料が2回発生する送料を注文単位でまとめ、2回目の出荷費用を負担するか、チェックアウト時に顧客へ通知してください。黙って二重請求することは避けてください
移動先で受領した数量が出荷数量と異なる一部受領に対応し、商品ごとに実際の受領数量を記録してください。輸送中の破損などの差異は別途処理します
拠点間で在庫が二重計上される各在庫レコードは拠点ごとに管理されます。在庫の合計を報告する際は、必ず各拠点の在庫を明示的に合算してください
フルフィルメントの途中で拠点がオフラインになる拠点を非アクティブにし、その拠点に割り当てられた未フルフィルメントの注文を再割り当てしてください
複数の追跡番号に顧客が戸惑うフルフィルメントごとに追跡メールを個別に送信し、「ご注文の全2件の出荷のうち、これは1件目です」と明記してください

関連スキル

  • @inventory-tracking
  • @low-stock-alerts
  • @catalog-import-export

コメント