2026年8月23日

2026年8月23日

WooCommerce Bookingsのエラーを解決する方法

はじめに

WooCommerce Bookingsで商品ページの予約カレンダーが表示されない・特定の日付を選択しても「この日付は利用できません」と表示され予約できない・予約完了後に確認メールが届かない・バッファータイムが設定通りに機能しないといった問題は、タイムゾーン設定のズレ・予約可能期間の設定ミス・リソースの競合が原因です。

症状・原因

  • 商品ページに「予約カレンダー」ウィジェットが表示されず、通常のカートボタンのみ表示される
  • カレンダーで日付を選択するとすべての時間帯が「利用不可」になっている
  • 予約が完了してもユーザーへの確認メールと管理者への通知メールが届かない
  • 1時間の予約後に30分のバッファーを設定したが連続予約が入ってしまう

解決手順

ステップ1:WooCommerce Bookingsの状態を確認する

# WooCommerce Bookingsの設定を確認
wp eval "
// バージョン確認
if (defined('WOOCOMMERCE_BOOKINGS_VERSION')) {
    echo 'WC Bookings version: ' . WOOCOMMERCE_BOOKINGS_VERSION . PHP_EOL;
}

// タイムゾーン設定確認
echo 'WP Timezone: '   . get_option('timezone_string') . PHP_EOL;
echo 'PHP Timezone: '  . date_default_timezone_get() . PHP_EOL;
echo 'Current time: '  . current_time('Y-m-d H:i:s') . PHP_EOL;

// 予約可能な商品を確認
\$bookable_products = get_posts([
    'post_type'      => 'product',
    'posts_per_page' => 10,
    'meta_key'       => '_wc_booking_duration',
]);
echo 'Bookable products: ' . count(\$bookable_products) . PHP_EOL;

foreach (\$bookable_products as \$p) {
    \$duration   = get_post_meta(\$p->ID, '_wc_booking_duration', true);
    \$min_date   = get_post_meta(\$p->ID, '_wc_booking_min_date', true);
    \$max_date   = get_post_meta(\$p->ID, '_wc_booking_max_date', true);
    \$buffer     = get_post_meta(\$p->ID, '_wc_booking_buffer_period', true);
    printf('  Product #%d | %s | Duration: %s min | Buffer: %s min | Min: %s days | Max: %s days' . PHP_EOL,
        \$p->ID, \$p->post_title, \$duration, \$buffer, \$min_date, \$max_date);
}

// 最近の予約を確認
\$bookings = get_posts([
    'post_type'      => 'wc_booking',
    'posts_per_page' => 5,
    'post_status'    => 'any',
]);
echo 'Recent bookings: ' . count(\$bookings) . PHP_EOL;
foreach (\$bookings as \$b) {
    printf('  #%d | Status: %s | Start: %s' . PHP_EOL,
        \$b->ID, \$b->post_status,
        get_post_meta(\$b->ID, '_booking_start', true));
}
"

ステップ2:予約の作成と管理をカスタマイズする

// functions.php: WC Bookings予約管理

// ① 予約作成時のカスタム処理
add_action('woocommerce_booking_created', function(int $booking_id): void {
    $booking = new WC_Booking($booking_id);
    $order   = $booking->get_order();

    error_log(sprintf('[WCB] Booking #%d created | Product: %d | Start: %s | Customer: %s',
        $booking_id,
        $booking->get_product_id(),
        $booking->get_start_date(),
        $order ? $order->get_billing_email() : 'N/A'
    ));

    // 予約に追加メモを付ける
    $booking->update_meta('_custom_booking_note', '自動処理済み');
});

// ② 予約ステータス変更時の処理
add_action('woocommerce_booking_status_changed', function(int $booking_id, string $old_status, string $new_status): void {
    error_log(sprintf('[WCB] Booking #%d: %s → %s', $booking_id, $old_status, $new_status));

    // 予約がキャンセルされたらリソースを解放
    if ($new_status === 'cancelled') {
        $booking   = new WC_Booking($booking_id);
        $person_id = $booking->get_person_id();
        if ($person_id) {
            // リソースのロックを解除するカスタム処理
            delete_transient('booking_lock_' . $person_id . '_' . $booking->get_start());
        }
    }
}, 10, 3);

// ③ 予約可能スロットをプログラムで確認
function check_booking_availability(int $product_id, string $date): array {
    $product = new WC_Product_Booking($product_id);
    if (!$product) return ['available' => false, 'slots' => []];

    $slots    = $product->get_available_slots($date);
    $duration = (int) get_post_meta($product_id, '_wc_booking_duration', true);

    return [
        'available' => !empty($slots),
        'slots'     => $slots,
        'duration'  => $duration,
    ];
}

ステップ3:カレンダー表示の問題を修正する

// functions.php: カレンダー表示カスタマイズ

// ① 予約可能な最小日数を動的に変更
add_filter('woocommerce_booking_get_min_date', function(array $min_date, object $product): array {
    // 当日予約を許可しない(最低1日前まで)
    $min_date['value'] = 1;
    $min_date['unit']  = 'day';
    return $min_date;
}, 10, 2);

// ② 予約可能な最大日数を変更
add_filter('woocommerce_booking_get_max_date', function(array $max_date, object $product): array {
    // 最大90日先まで予約可能
    $max_date['value'] = 90;
    $max_date['unit']  = 'day';
    return $max_date;
}, 10, 2);

// ③ 特定の曜日を予約不可にする
add_filter('wc_bookings_is_date_fully_booked', function(bool $fully_booked, int $start, int $end, object $product): bool {
    // 日曜日(0)と土曜日(6)は予約不可
    $day_of_week = (int) date('w', $start);
    if (in_array($day_of_week, [0, 6], true)) {
        return true; // 予約不可として扱う
    }
    return $fully_booked;
}, 10, 4);

// ④ 祝日を予約不可に設定
add_filter('wc_bookings_is_date_fully_booked', function(bool $fully_booked, int $start): bool {
    $holidays = [
        '2024-01-01', '2024-01-08', '2024-02-11', // 元旦・成人の日・建国記念日
        '2024-03-20', '2024-04-29', '2024-05-03',
    ];
    $date = date('Y-m-d', $start);
    return $fully_booked || in_array($date, $holidays, true);
}, 11, 2);

ステップ4:予約メールと通知をカスタマイズする

// functions.php: 予約メール設定

// ① 予約確認メールをカスタマイズ
add_filter('woocommerce_email_subject_customer_booking', function(string $subject, object $booking): string {
    return sprintf('[%s] ご予約確認 #%d', get_bloginfo('name'), $booking->get_id());
}, 10, 2);

// ② 予約リマインダーメールをスケジュール
add_action('woocommerce_booking_confirmed', function(int $booking_id): void {
    $booking    = new WC_Booking($booking_id);
    $start_time = $booking->get_start();

    // 予約1日前にリマインダーをスケジュール
    $reminder_time = $start_time - DAY_IN_SECONDS;
    if ($reminder_time > time()) {
        wp_schedule_single_event($reminder_time, 'send_booking_reminder', [$booking_id]);
    }
});

add_action('send_booking_reminder', function(int $booking_id): void {
    $booking = new WC_Booking($booking_id);
    $order   = $booking->get_order();
    if (!$order) return;

    wp_mail(
        $order->get_billing_email(),
        '明日のご予約のご確認',
        sprintf('明日 %s のご予約をお忘れなく。', $booking->get_start_date()),
        ['Content-Type: text/html; charset=UTF-8']
    );
});

// ③ 予約キャンセル時の返金処理
add_action('woocommerce_booking_cancelled', function(int $booking_id): void {
    $booking = new WC_Booking($booking_id);
    $order   = $booking->get_order();
    if (!$order) return;

    // 予約の3日以上前のキャンセルは全額返金
    $days_until = ($booking->get_start() - time()) / DAY_IN_SECONDS;
    if ($days_until >= 3) {
        $refund = wc_create_refund([
            'amount'   => $order->get_total(),
            'order_id' => $order->get_id(),
            'reason'   => '予約キャンセル(3日以上前)',
        ]);
        error_log(sprintf('[WCB] Auto-refund for booking #%d: %s', $booking_id,
            is_wp_error($refund) ? $refund->get_error_message() : 'success'));
    }
});

ステップ5:予約データの一括操作

# 予約の状態を一括確認・操作
wp eval "
// ステータス別の予約数を確認
\$statuses = ['confirmed', 'pending', 'cancelled', 'complete', 'in-cart', 'was-in-cart'];
echo '=== Booking Status Summary ===' . PHP_EOL;
foreach (\$statuses as \$status) {
    \$count = get_posts([
        'post_type'   => 'wc_booking',
        'post_status' => 'wc-' . \$status,
        'numberposts' => -1,
        'fields'      => 'ids',
    ]);
    printf('  %-20s: %d' . PHP_EOL, \$status, count(\$count));
}

// 期限切れ(過去)の予約を確認
\$past_bookings = get_posts([
    'post_type'      => 'wc_booking',
    'post_status'    => 'wc-confirmed',
    'numberposts'    => 10,
    'meta_query'     => [[
        'key'     => '_booking_end',
        'value'   => current_time('timestamp'),
        'compare' => '<',
        'type'    => 'NUMERIC',
    ]],
]);
echo 'Past confirmed bookings not marked complete: ' . count(\$past_bookings) . PHP_EOL;
"

注意事項

  • WooCommerce Bookingsのカレンダーが表示されない場合は、まずブラウザの開発者ツールでJavaScriptエラーがないか確認してください。次に「WooCommerce → 設定 → 予約」でタイムゾーン設定がWordPressのタイムゾーン設定と一致しているか確認してください
  • バッファータイムが機能しない場合は、_wc_booking_buffer_periodメタフィールドの値(分単位)が正しく設定されているかget_post_meta()で確認してください。Bookingsの管理画面では「バッファー期間」の単位(分/時間)の設定ミスが多くあります
  • 大量の予約データがある場合、WC_Product_Booking::get_available_slots()の実行に時間がかかりページ表示が遅くなります。Bookingキャッシュを有効化するか、よく参照される商品の空き状況をTransientでキャッシュしてください

まとめ

WooCommerce Bookings修復は①_wc_booking_duration_wc_booking_buffer_periodメタの確認・タイムゾーンの整合性確認、②woocommerce_booking_createdフックで予約作成ログ・woocommerce_booking_status_changedフックでキャンセル時リソース解放、③woocommerce_booking_get_min_date/get_max_dateフィルターで予約可能期間調整・wc_bookings_is_date_fully_bookedフィルターで祝日・週末を予約不可に設定、④woocommerce_booking_confirmedフックで1日前リマインダーをWP-Cronでスケジュール・woocommerce_booking_cancelledフックでキャンセルポリシーに基づく自動返金、⑤ステータス別集計SQLで予約状況を一括管理する手順で解決します。

お気軽にご相談ください

お見積りへ お問い合わせへ