inventory-tracking について
Shopify、WooCommerce、BigCommerceやカスタム環境での在庫設定を扱う、EC運営者や開発者向けのスキルです。在庫予約、売り越し防止、複数拠点の在庫、バックオーダーを中心に説明します。
主な機能
- 01標準の在庫追跡設定
- 02在庫予約と売り越し防止
- 03複数拠点の在庫設定
- 04バックオーダーの管理
- 05低在庫アラートの設定
活用シーン
- 01在庫切れ商品の受注を調査
- 02倉庫ごとの在庫数を設定
- 03限定商品の同時注文に対応
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ finsilabs/ awesome-ecommerce-skills --skill inventory-trackingインストール後、Claude Code で/inventory-trackingを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
tile.jsonevals/atomic-reservation-with-optimistic-concu/criteria.jsontask.mdevals/cart-expiry-job-and-idempotent-release/criteria.jsontask.mdevals/inventory-data-model-and-multi-warehouse/criteria.jsontask.md詳細
在庫追跡
概要
リアルタイムの在庫追跡では、顧客が商品をカートに追加した時点で在庫を引き当て、注文のフルフィルメント時に在庫数を減らすことで、過剰販売を防ぎます。主要な EC プラットフォームには、ほぼすべてこの機能が組み込まれています。通常は、まずプラットフォーム標準の在庫追跡を利用するのが最適です。プラットフォームでは満たせない要件(複雑な複数倉庫への振り分け、独自の引き当て期間、外部 WMS との統合など)がある場合に限り、カスタムの在庫ロジックを構築してください。
このスキルを使用する場面
- 在庫切れの商品に注文が入り、過剰販売が発生している場合
- ロケーションごとの在庫数を管理する複数倉庫の在庫を実装する場合
- 在庫切れ商品の取り寄せ注文や予約注文の機能を構築する場合
- フラッシュセールや商品発売により、在庫数が限られた商品のチェックアウトが高い同時実行数で行われる場合
基本手順
ステップ 1:プラットフォームを特定し、在庫追跡を有効にする
| プラットフォーム | 標準の在庫機能 | 推奨拡張機能 |
|---|---|---|
| Shopify | ロケーションに対応した、バリエーションごとの標準在庫追跡 | 発注書や需要予測には Stocky(無料、Shopify 提供) |
| WooCommerce | 取り寄せ注文に対応した標準の在庫管理 | 高度な複数倉庫管理や仕入先向け発注書には ATUM Inventory Management |
| BigCommerce | SKU ごとの標準在庫追跡と在庫僅少アラート | 倉庫への振り分けには Multi-Location Inventory アプリ |
| カスタム/ヘッドレス | 楽観的ロックを用いたアトミックな引き当てを構築 | 標準の在庫管理機能がないカスタムプラットフォームでは必須 |
ステップ 2:プラットフォームごとの設定
Shopify
Shopify は標準で、バリエーションごと、ロケーションごとに在庫を追跡します。
在庫追跡を有効にする:
- 管理画面 → 商品 → [商品] → [バリエーション] に移動します
- 在庫 で 数量を追跡する にチェックを入れます
- 各ロケーションの数量を入力します
- 「在庫切れでも販売を続ける」は、その商品で取り寄せ注文を許可する場合に限りチェックを入れます
ロケーションを設定する:
- 設定 → ロケーション に移動します
- 各倉庫、店舗、フルフィルメントセンターをロケーションとして追加します
- 商品バリエーションの編集時に、各ロケーションの数量を個別に設定します
取り寄せ注文:
- バリエーションごとに有効にするには、バリエーションで 在庫切れでも販売を続ける にチェックを入れます
- または、より細かく管理するには、Pre-Order Now や Back In Stock などの取り寄せ注文アプリを使用します(入荷時の顧客への通知、予約注文の受付など)
アクセスが集中する際の過剰販売防止:
Shopify のチェックアウトシステムは、2 人の顧客が最後の 1 点を同時に購入することを防ぐため、チェックアウト処理中に在庫を確保します。フラッシュセールでは、Shopify Scripts(Plus)または Inventory Planner アプリを使用して購入数の上限を設定してください。
実店舗との在庫同期:
- 発注書や入荷処理には Stocky(無料、Shopify 提供)をインストールします
- 発注書の入荷時に、Stocky が Shopify の在庫を自動的に更新します
- Shopify POS アプリを使用して、オンラインストアと実店舗の間で在庫を同期します
WooCommerce
WooCommerce には在庫管理機能が組み込まれています。
在庫追跡を有効にする:
- WooCommerce → Settings → Products → Inventory に移動します
- Enable stock management にチェックを入れます
- Hold stock (minutes) を設定します。これはチェックアウト中に在庫を確保する時間です(デフォルト:60分)
商品ごとの設定:
- WooCommerce → Products → [Product] → Inventory tab に移動します
- Manage stock? を有効にします
- Stock quantity を入力します
- Backorders を設定します:「Do not allow」/「Allow, but notify customer」/「Allow」
- この商品の Low stock threshold を設定します
ATUM による高度な在庫管理:
- ATUM Inventory Management for WooCommerce をインストールします(基本機能は無料、高度な機能は有料です)
- ATUM では、すべての商品の在庫レベルを表示する一元管理の在庫ダッシュボードを利用できます
- ATUM → Purchase Orders → Add PO から発注書を追加します
- ATUM で発注書を受領すると、WooCommerce の在庫が自動的に増加します
- 複数倉庫に対応するには、ATUM の Multi-Inventory アドオン(有料)を使って、場所ごとに在庫を割り当て、フルフィルメントを振り分けます
BigCommerce
BigCommerce は SKU ごとに在庫をネイティブに追跡します。
在庫追跡を有効にする:
- Products → [Product] → Inventory tab に移動します
- Inventory tracking を「By product」または「By option」(バリエーションの場合)に設定します
- 現在の在庫数を入力します
- アラート用に Low stock level を設定します
複数拠点の在庫管理:
- BigCommerce App Marketplace から Multi-Location Inventory アプリをインストールします
- 拠点ごとに在庫数を割り当てます
- フルフィルメントの振り分けルール(最寄りの拠点から発送、最も安い拠点から発送など)を設定します
バックオーダー:
- BigCommerce はネイティブで対応しています。在庫切れ後も販売を続ける商品で Allow backorders を設定します
- バックオーダー時には、商品ページに「Ships in X days」と表示されます
カスタム/ヘッドレス
カスタムプラットフォームでは、高負荷時の過剰販売を防ぐため、楽観的同時実行制御を用いたアトミックな在庫予約を実装します:
// lib/inventory.ts
const MAX_RETRIES = 3;
// Reserve inventory atomically — handles concurrent requests safely
export async function reserveInventory({
variantId, locationId, quantity, referenceId
}: { variantId: string; locationId: string; quantity: number; referenceId: string }) {
for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
const level = await db.inventoryLevels.findUnique({
where: { variantId_locationId: { variantId, locationId } },
});
if (!level) throw new Error(`Inventory not found: ${variantId}`);
const available = level.onHand - level.reserved;
if (available < quantity && !level.backorderAllowed) {
throw new Error(`Insufficient stock: ${available} available, ${quantity} requested`);
}
// Optimistic update — only succeeds if version hasn't changed (no concurrent modifications)
const updated = await db.inventoryLevels.updateMany({
where: { variantId_locationId: { variantId, locationId }, version: level.version },
data: { reserved: level.reserved + quantity, version: level.version + 1 },
});
if (updated.count === 0) {
// Another process modified inventory concurrently; retry
await new Promise(r => setTimeout(r, 50 * (attempt + 1)));
continue;
}
// Log the transaction for audit trail
await db.inventoryTransactions.create({
data: { variantId, locationId, type: 'reserve', quantity: -quantity, referenceId },
});
return { success: true, remaining: available - quantity };
}
throw new Error(`Failed to reserve inventory after ${MAX_RETRIES} retries`);
}
// Release reservation when cart expires or order is cancelled
export async function releaseReservation({
variantId, locationId, quantity, referenceId
}: { variantId: string; locationId: string; quantity: number; referenceId: string }) {
// Idempotency check — don't release twice
const existing = await db.inventoryTransactions.findFirst({
where: { type: 'release', referenceId, variantId },
});
if (existing) return;
await db.$transaction([
db.inventoryLevels.update({
where: { variantId_locationId: { variantId, locationId } },
data: { reserved: { decrement: quantity } },
}),
db.inventoryTransactions.create({
data: { variantId, locationId, type: 'release', quantity: +quantity, referenceId },
}),
]);
}
// Expire stale cart reservations — run every 5-10 minutes via cron
export async function expireStaleCartReservations() {
const TTL_MINUTES = 30;
const cutoff = new Date(Date.now() - TTL_MINUTES * 60 * 1000);
const staleCarts = await db.carts.findMany({
where: { status: 'active', updatedAt: { lt: cutoff }, reservedAt: { not: null } },
include: { items: true },
});
for (const cart of staleCarts) {
for (const item of cart.items) {
await releaseReservation({ variantId: item.variantId, locationId: item.locationId, quantity: item.quantity, referenceId: cart.id });
}
}
}
ステップ 3:バックオーダーの動作を設定する
バックオーダーを許可すると、在庫がゼロでもお客様が商品を購入でき、配送が遅れることを明確に伝えられます。
バックオーダーを許可するケース:
- 仕入先の納期が安定している商品(7~14日)
- 受注生産の商品
- 販売予定商品の予約販売キャンペーン
バックオーダーを禁止するケース:
- 供給が不安定な商品
- 再入荷を自社で管理できない、第三者がフルフィルメントを行う商品
コミュニケーションのベストプラクティス:
- 在庫が 0 でバックオーダーが有効な場合、商品ページに「7~10日で発送」と表示します
- 注文確認メールに発送予定日を記載します
- 予定日に変更があった場合は、事前にお客様へ通知します
ステップ 4:在庫アラートを設定する
在庫追跡と在庫僅少アラートを組み合わせます。詳しい設定方法については、@low-stock-alerts スキルを参照してください。概要は次のとおりです:
- Shopify:Admin → Products に移動します。Shopify には在庫僅少インジケーターが表示されます。メールアラートには Stocky を使用します
- WooCommerce:WooCommerce → Settings → Products → Inventory → Low stock threshold に移動します。在庫がこのしきい値を下回ると、WooCommerce からストア管理者にメールが送信されます
- BigCommerce:Products → [Product] → Inventory → Low stock level に移動します。BigCommerce からメール通知が自動送信されます
ベストプラクティス
- すべての SKU で在庫追跡を有効にする — 在庫追跡のない商品は、気づかないうちに過剰販売される可能性があります。追跡を無効にするのは、デジタル商品または在庫に上限がない商品のみにしてください
- 決済手続き中の在庫予約時間を設定する — WooCommerce の「Hold stock」設定(デフォルトは 60 min)により、放棄されたカートによって在庫が無期限に確保されるのを防げます
- 過剰販売の防止をテストする — フラッシュセールの準備中に、2 件の決済手続きを同時にシミュレートし、最後の 1 点を二重に購入できないことを手動で確認してください
- 在庫の変更をすべて記録する — プラットフォームでは標準機能で記録されます。独自に構築する場合、差異の原因を調査するには、変更不可の
inventory_transactionsテーブルが不可欠です - サードパーティ製アプリを購入する前に、プラットフォーム組み込みの複数拠点在庫管理を使用する — Shopify、WooCommerce、BigCommerce はいずれも、複数拠点を標準機能で管理できます
よくある問題
| 問題 | 解決策 |
|---|---|
| Shopify のフラッシュセール中に過剰販売が発生する | Shopify では決済フロー中に在庫が確保されます。非常に多くの同時アクセスが見込まれる販売開始時には、Shopify Scripts(Plus)で購入数の上限を設定するか、ウェイトリストアプリを使用してください |
| 注文後に WooCommerce の在庫が減らない | 商品と WooCommerce の全般設定の両方で、在庫管理が有効になっていることを確認してください。両方を有効にする必要があります |
| 商品の出荷前に注文がキャンセルされると、在庫がすぐに戻される | 物理商品の場合、これは正しい動作です。戻された在庫は、ほかのお客様が購入できるようになります。取り寄せ商品に限り、在庫の戻しを遅らせてください |
| 手動調整後に在庫がマイナスになる | WooCommerce(ATUM)で検証を追加するか、独自に構築する場合は DB のチェック制約を設定してください。reserved >= 0 および on_hand >= 0 |
| POS から Shopify の拠点に在庫更新が届かない | Settings → Locations → POS channel で、POS が正しい Shopify の拠点に接続されていることを確認してください |
関連スキル
- @multi-warehouse
- @low-stock-alerts
- @variant-matrix