WooCommerce: автоматическое создание возвратов товаров по счетчику продаж

Проблема: как автоматически создавать возвраты товаров при превышении лимита продаж

В магазинах на WooCommerce иногда требуется автоматизировать процесс возврата товаров, если определённый товар продаётся свыше установленного количества. Это может понадобиться для контроля остатков, предотвращения перепродаж или обработки специальных условий возврата. В стандартном WooCommerce такой функционал отсутствует, поэтому нужно реализовать его самостоятельно с использованием хуков и кастомного кода.

Диагностика текущей ситуации

Перед внедрением автоматических возвратов важно проверить:

  • Есть ли у товара мета-поле или свойство для лимита продаж.
  • Отслеживается ли количество продаж товара в базе WooCommerce.
  • Какие статусы заказов считаются подтверждёнными для подсчёта продаж (обычно "завершён", "обработан").
  • Настроена ли система возвратов (Refunds) в WooCommerce и активированы ли необходимые права у пользователя для создания возвратов.

Пошаговое решение: автоматическое создание возвратов при превышении лимита

1. Добавляем мета-поле для лимита продаж товара

Для удобства создадим пользовательское поле sales_limit, в котором укажем максимальное количество продаж для каждого товара.

add_action('woocommerce_product_options_inventory_product_data', function() {
    woocommerce_wp_text_input( array(
        'id' => 'sales_limit',
        'label' => __('Лимит продаж', 'woocommerce'),
        'desc_tip' => true,
        'description' => __('Максимальное количество продаж для этого товара', 'woocommerce'),
        'type' => 'number',
        'custom_attributes' => array('min' => 0)
    ) );
});

add_action('woocommerce_process_product_meta', function($post_id) {
    $limit = isset($_POST['sales_limit']) ? intval($_POST['sales_limit']) : 0;
    update_post_meta($post_id, 'sales_limit', $limit);
});

2. Создаем функцию подсчёта продаж товара

Подсчитаем количество проданных единиц товара в заказах со статусами completed и processing.

function get_product_sales_count($product_id) {
    global $wpdb;
    $statuses = array('wc-completed', 'wc-processing');
    $placeholders = implode(',', array_fill(0, count($statuses), '%s'));

    $query = $wpdb->prepare(
        "SELECT SUM(oi.meta_value + 0) FROM {$wpdb->prefix}woocommerce_order_items oi
         
         INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta oim ON oi.order_item_id = oim.order_item_id
         INNER JOIN {$wpdb->posts} p ON oi.order_id = p.ID
         WHERE p.post_type = 'shop_order'
         AND p.post_status IN ($placeholders)
         AND oim.meta_key = '_product_id'
         AND oim.meta_value = %d
         AND oi.order_item_type = 'line_item'",
        array_merge($statuses, array($product_id))
    );

    $count = $wpdb->get_var($query);
    return intval($count);
}

3. Автоматически создаём возврат при превышении лимита

Подключаем хук, который срабатывает при смене статуса заказа. Проверяем каждый товар, и если продажи превысили лимит, создаём возврат.

add_action('woocommerce_order_status_changed', function($order_id, $old_status, $new_status) {
    if (!in_array($new_status, array('completed', 'processing'))) {
        return; // Проверяем только нужные статусы
    }

    $order = wc_get_order($order_id);

    foreach ($order->get_items() as $item_id => $item) {
        $product_id = $item->get_product_id();
        $limit = intval(get_post_meta($product_id, 'sales_limit', true));
        if ($limit <= 0) {
            continue; // Нет лимита, пропускаем
        }

        $sales_count = get_product_sales_count($product_id);

        if ($sales_count > $limit) {
            // Создаём возврат на количество превышения
            $refund_qty = $sales_count - $limit;
            $refund_amount = $item->get_total() / $item->get_quantity() * $refund_qty;

            // Проверяем, не был ли уже сделан возврат по этому товару в этом заказе
            $refunds = $order->get_refunds();
            $already_refunded = false;
            foreach ($refunds as $refund) {
                foreach ($refund->get_items() as $refund_item) {
                    if ($refund_item->get_product_id() == $product_id) {
                        $already_refunded = true;
                        break 2;
                    }
                }
            }

            if ($already_refunded) {
                continue; // Уже есть возврат для этого товара
            }

            $refund = wc_create_refund(array(
                'amount' => wc_format_decimal($refund_amount, 2),
                'reason' => 'Автоматический возврат из-за превышения лимита продаж',
                'order_id' => $order_id,
                'line_items' => array(
                    $item_id => array('qty' => $refund_qty),
                ),
                'refund_payment' => true,
            ));
            if (is_wp_error($refund)) {
                error_log('Ошибка создания возврата: ' . $refund->get_error_message());
            }
        }
    }
}, 10, 3);

Как проверить, что автоматический возврат работает

  • Задайте лимит продаж для тестового товара в админке (например, 5).
  • Создайте несколько заказов с этим товаром, чтобы суммарное количество превысило лимит.
  • Измените статус заказа на "Завершён" или "В обработке".
  • Проверьте, что в заказе автоматически появился возврат на превышающую количество.
  • Проверьте логи ошибок для исключений.

Частые ошибки и их исправление

  • Возвраты не создаются: Убедитесь, что функция wc_create_refund доступна и у пользователя есть права на создание возвратов.
  • Возврат создается несколько раз: Добавлена проверка на существующий возврат по товару, но если логика меняется, её нужно адаптировать.
  • Неверный расчёт суммы возврата: Важно корректно делить сумму на количество товара, чтобы вернуть пропорциональную сумму.
  • Производительность: При большом количестве заказов и товаров запрос к базе может быть медленным, оптимизируйте запросы или кэшируйте результаты.

Практические советы по безопасности и производительности

  • Используйте nonce и проверяйте права доступа, если расширяете функционал для интерфейса.
  • Кэшируйте результаты подсчёта продаж, чтобы не выполнять тяжелые SQL-запросы на каждый статус заказа.
  • Логи ошибок пишите в отдельный файл или используйте специализированные сервисы для мониторинга.
  • Тестируйте на staging-среде перед внедрением в продакшен.

Сравнение вариантов реализации автоматических возвратов

МетодПлюсыМинусыКомпромисс
Плагин возвратов с настройками лимитовГотовое решение, поддержкаЦена, ограниченная кастомизацияИспользовать для стандартных задач
Кастомный код с хуками WooCommerceПолный контроль, адаптация под задачиНеобходимость технических знаний, поддержкаПодходит для разработчиков
Ручное создание возвратовПростотаТрудоёмко, риск ошибокИспользовать для небольших магазинов
Как отследить посещаемость сайта на WordPress: лучшие методы и плагины
01.12.2025
Как установить лимит поисковых запросов в WordPress
04.03.2026
Как создать список задач с отметкой выполнено в WordPress с примером кода
04.04.2026
Как создать автообновляемые KPI отчеты в WordPress с примерами и плагинами
28.02.2026
Как отключить отображение текстовых блоков в WordPress без удаления
13.12.2025