2026年10月13日
2026年10月13日
WooCommerceの在庫管理エラーを解決する方法
はじめに
WooCommerceの在庫管理で「在庫が減らない」「在庫切れなのに購入できる」「在庫数が注文数と合わない」問題は、注文ステータスの設定・在庫管理の有効化漏れ・同時注文によるレースコンディションが原因です。
症状・原因
- 注文が完了しても在庫数が減らない
- 在庫0なのにカートに追加できる・注文できる
- 複数の注文が同時に入ると在庫がマイナスになる
- バリエーション商品の在庫だけ正しく管理されない
解決手順
ステップ1:在庫設定の現状を確認する
# 在庫管理の全体設定を確認
wp option get woocommerce_manage_stock # yes/no
wp option get woocommerce_stock_email_recipient
wp option get woocommerce_notify_low_stock_amount
# 特定商品の在庫状態を確認
wp eval "
\$product = wc_get_product(123);
printf(
'Stock managed: %s, Qty: %s, Status: %s' . PHP_EOL,
\$product->get_manage_stock() ? 'yes' : 'no',
\$product->get_stock_quantity(),
\$product->get_stock_status()
);
"
# 在庫が減らない注文を確認
wp eval "
\$orders = wc_get_orders(['status' => 'processing', 'limit' => 10]);
foreach (\$orders as \$order) {
printf('Order %d: %s, stock_reduced=%s' . PHP_EOL,
\$order->get_id(),
\$order->get_status(),
\$order->get_meta('_order_stock_reduced') ?: 'no'
);
}
"
ステップ2:在庫管理を正しく設定する
// functions.php: 在庫管理の設定を確認・強制する
// ① 全商品の在庫管理を一括で有効化
add_action('woocommerce_product_set_stock', function(WC_Product $product): void {
// 在庫変更時にログ
WC()->logger()->info(
sprintf('Stock updated for product %d: qty=%s status=%s',
$product->get_id(),
$product->get_stock_quantity(),
$product->get_stock_status()
),
['source' => 'stock-manager']
);
});
// ② 注文確定時に在庫を確実に減らす
add_action('woocommerce_checkout_order_processed', function(int $order_id): void {
$order = wc_get_order($order_id);
if (!$order) return;
// 既に在庫が減らされているか確認
if (!$order->get_meta('_order_stock_reduced')) {
wc_reduce_stock_levels($order_id);
$order->update_meta_data('_order_stock_reduced', 'yes');
$order->save();
}
}, 10);
// ③ 注文キャンセル時に在庫を戻す
add_action('woocommerce_order_status_cancelled', function(int $order_id): void {
$order = wc_get_order($order_id);
if (!$order || !$order->get_meta('_order_stock_reduced')) {
return;
}
wc_restock_refunded_items($order, $order->get_items());
$order->delete_meta_data('_order_stock_reduced');
$order->save();
$order->add_order_note('キャンセルにより在庫を復元しました。');
});
ステップ3:在庫のずれを修正する
// functions.php: 在庫数を手動で修正する
function fix_product_stock(int $product_id, int $correct_quantity): void {
$product = wc_get_product($product_id);
if (!$product) {
return;
}
$current = $product->get_stock_quantity();
// 在庫数を正しい値に設定
wc_update_product_stock($product, $correct_quantity, 'set');
WC()->logger()->info(
sprintf('Stock fixed for product %d: %d → %d',
$product_id, $current, $correct_quantity),
['source' => 'stock-fix']
);
}
# WP-CLI で在庫を一括更新
wp eval "
// 全商品の在庫状態を確認・修正
\$products = wc_get_products(['limit' => -1, 'manage_stock' => true]);
foreach (\$products as \$product) {
\$qty = \$product->get_stock_quantity();
if (\$qty < 0) {
echo '在庫マイナス: ' . \$product->get_name() . ' (' . \$qty . ')' . PHP_EOL;
// wc_update_product_stock(\$product, 0, 'set'); // 0にリセットする場合
}
}
"
# 特定商品の在庫を直接設定
wp eval "
\$product = wc_get_product(123);
wc_update_product_stock(\$product, 50, 'set');
echo '在庫を50に設定しました';
"
ステップ4:同時注文のレースコンディションを防ぐ
// functions.php: データベースロックで同時注文の在庫競合を防ぐ
add_filter('woocommerce_checkout_cart_item_quantity', function(int $qty, array $item): int {
return $qty;
}, 10, 2);
// 在庫チェックをより厳密に行う
add_filter('woocommerce_is_purchasable', function(bool $purchasable, WC_Product $product): bool {
if (!$purchasable) {
return false;
}
if ($product->managing_stock()) {
$stock = $product->get_stock_quantity();
// データベースから最新の在庫を直接取得
global $wpdb;
$db_stock = $wpdb->get_var($wpdb->prepare(
"SELECT meta_value FROM {$wpdb->postmeta}
WHERE post_id = %d AND meta_key = '_stock'",
$product->get_id()
));
if ((int) $db_stock <= 0 && !$product->backorders_allowed()) {
return false;
}
}
return $purchasable;
}, 10, 2);
// 在庫予約(注文処理中に仮押さえ)
add_action('woocommerce_checkout_order_created', function(WC_Order $order): void {
foreach ($order->get_items() as $item) {
$product = $item->get_product();
if ($product && $product->managing_stock()) {
// Transient で一時的に在庫を予約
$key = 'stock_reserve_' . $product->get_id();
$reserved = (int) get_transient($key);
set_transient($key, $reserved + $item->get_quantity(), 300); // 5分間予約
}
}
});
ステップ5:バックオーダーと在庫切れ通知を設定する
// functions.php: 在庫切れ・低在庫の通知をカスタマイズ
// 低在庫通知のしきい値を商品ごとに設定
add_filter('woocommerce_get_low_stock_amount', function(int $amount, WC_Product $product): int {
// カテゴリに応じてしきい値を変更
if (has_term('popular', 'product_cat', $product->get_id())) {
return 10; // 人気商品は10個で通知
}
return $amount; // デフォルト
}, 10, 2);
// バックオーダー時のメッセージをカスタマイズ
add_filter('woocommerce_get_availability', function(array $availability, WC_Product $product): array {
if ($product->is_on_backorder()) {
$availability['availability'] = '入荷待ち(約2週間でお届け)';
$availability['class'] = 'available-on-backorder';
}
return $availability;
}, 10, 2);
// 在庫復元時に自動でバックオーダー注文を処理
add_action('woocommerce_product_set_stock', function(WC_Product $product): void {
if ($product->get_stock_quantity() <= 0) {
return;
}
// バックオーダー中の注文があれば管理者に通知
$backorders = wc_get_orders([
'status' => 'on-hold',
'meta_key' => '_backorder_product_id',
'meta_value' => $product->get_id(),
'limit' => 10,
]);
if (!empty($backorders)) {
wp_mail(
get_option('admin_email'),
'在庫復元:バックオーダー注文を処理してください',
sprintf('商品「%s」の在庫が補充されました。%d件のバックオーダーがあります。',
$product->get_name(), count($backorders))
);
}
});
注意事項
wc_reduce_stock_levels()は注文ステータスがprocessingまたはcompletedに変わったときに自動で呼ばれます。カスタムステータスを使っている場合は手動で呼び出すか、woocommerce_payment_complete_order_statusフィルターで対応してください- 大量注文が同時に発生する場合、MySQLのトランザクションとSELECT FOR UPDATEを使った在庫ロックが必要です。WooCommerceは内部でこれを処理しますが、カスタムコードで在庫を直接操作する場合は注意してください
- バックオーダーを許可する場合は「設定 → 商品 → 在庫 → バックオーダー」の設定と商品個別の設定の両方を確認してください
まとめ
WooCommerce在庫エラーの解決は①wc_get_product()->get_stock_quantity()と_order_stock_reducedメタで在庫状態を確認、②manage_stock=trueとwc_reduce_stock_levels()で確実に在庫を減らす処理を実装、③wc_update_product_stock()で在庫数をデータベースから直接修正、④is_purchasableフィルターでDBから最新在庫を取得してレースコンディションを防止、⑤woocommerce_get_availabilityフィルターでバックオーダー表示と在庫復元通知をカスタマイズします。