Проблема: как автоматически создавать возвраты товаров при превышении лимита продаж
В магазинах на 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 | Полный контроль, адаптация под задачи | Необходимость технических знаний, поддержка | Подходит для разработчиков |
| Ручное создание возвратов | Простота | Трудоёмко, риск ошибок | Использовать для небольших магазинов |