Диагностика проблемы: почему важно обновлять наличие товаров автоматически
В WooCommerce стандартное поведение управления запасами происходит при оформлении заказа или вручную через админку. Однако при изменении заказа после его создания (например, администратор меняет количество товаров или добавляет/удаляет позиции) складские запасы не всегда обновляются корректно. Это приводит к рассинхронизации наличия и фактических остатков, что вызывает проблемы с продажами и учетными данными.
Основные признаки проблемы:
- После изменения заказа вручную в админке складские запасы не корректируются;
- В отчётах по запасам отображаются неверные значения;
- Клиенты могут заказать товар с отрицательным запасом;
- Автоматические уведомления о низком наличии не срабатывают вовремя.
Пошаговое решение: автоматическое обновление наличия при изменении заказа
1. Используем хуки WooCommerce для отслеживания изменений заказа
Для автоматического изменения наличия нужно отследить момент сохранения заказа и сравнить старые и новые данные по товарам. Для этого применим хук woocommerce_update_order и функцию для получения предыдущего состояния заказа.
add_action('woocommerce_update_order', 'custom_update_stock_on_order_change', 10, 1);
function custom_update_stock_on_order_change($order_id) {
$order = wc_get_order($order_id);
if (!$order) return;
// Получаем старые данные заказа из базы
$old_order = wc_get_order($order_id);
$old_items = [];
foreach ($old_order->get_items() as $item) {
$old_items[$item->get_product_id()] = $item->get_quantity();
}
$new_items = [];
foreach ($order->get_items() as $item) {
$new_items[$item->get_product_id()] = $item->get_quantity();
}
// Сравниваем и обновляем запасы
// ... (логика ниже)
}2. Логика сравнения и корректировки запасов
Для каждого продукта в заказе вычисляем разницу между старым и новым количеством. Если количество увеличилось, уменьшаем склад; если уменьшилось — увеличиваем склад.
foreach ($new_items as $product_id => $new_qty) {
$old_qty = isset($old_items[$product_id]) ? $old_items[$product_id] : 0;
$diff = $new_qty - $old_qty;
if ($diff !== 0) {
$product = wc_get_product($product_id);
if ($product && $product->managing_stock()) {
$stock_qty = $product->get_stock_quantity();
$new_stock = $stock_qty - $diff; // Уменьшаем или увеличиваем
$product->set_stock_quantity($new_stock);
$product->save();
}
}
}
// Обрабатываем удалённые товары
foreach ($old_items as $product_id => $old_qty) {
if (!isset($new_items[$product_id])) {
$product = wc_get_product($product_id);
if ($product && $product->managing_stock()) {
$stock_qty = $product->get_stock_quantity();
$product->set_stock_quantity($stock_qty + $old_qty);
$product->save();
}
}
}3. Полный пример функции для functions.php
add_action('woocommerce_update_order', 'custom_update_stock_on_order_change', 10, 1);
function custom_update_stock_on_order_change($order_id) {
$order = wc_get_order($order_id);
if (!$order) return;
// Получаем предыдущие данные из базы напрямую
global $wpdb;
$old_items = [];
$results = $wpdb->get_results($wpdb->prepare(
"SELECT product_id, qty FROM {$wpdb->prefix}woocommerce_order_itemmeta AS meta
INNER JOIN {$wpdb->prefix}woocommerce_order_items AS items ON meta.order_item_id = items.order_item_id
WHERE items.order_id = %d AND meta.meta_key = '_qty'",
$order_id
));
foreach ($results as $row) {
$old_items[$row->product_id] = $row->qty;
}
$new_items = [];
foreach ($order->get_items() as $item) {
$new_items[$item->get_product_id()] = $item->get_quantity();
}
foreach ($new_items as $product_id => $new_qty) {
$old_qty = isset($old_items[$product_id]) ? $old_items[$product_id] : 0;
$diff = $new_qty - $old_qty;
if ($diff !== 0) {
$product = wc_get_product($product_id);
if ($product && $product->managing_stock()) {
$stock_qty = $product->get_stock_quantity();
$new_stock = $stock_qty - $diff;
$product->set_stock_quantity($new_stock);
$product->save();
}
}
}
foreach ($old_items as $product_id => $old_qty) {
if (!isset($new_items[$product_id])) {
$product = wc_get_product($product_id);
if ($product && $product->managing_stock()) {
$stock_qty = $product->get_stock_quantity();
$product->set_stock_quantity($stock_qty + $old_qty);
$product->save();
}
}
}
}Проверка результата после внедрения
Чтобы убедиться, что автоматическое обновление запасов работает корректно:
- Создайте тестовый заказ с несколькими товарами, у которых включено управление запасами.
- Подтвердите заказ и проверьте, что запасы уменьшились.
- Отредактируйте заказ в админке: измените количество товаров, добавьте или удалите позицию.
- Сохраните заказ и проверьте изменения запасов в карточках товаров.
- Проверьте, что запасы не уходят в отрицательные значения без разрешения WooCommerce.
Частые ошибки и как их исправить
- Запасы не изменяются после редактирования заказа: возможно, не подключен хук или функция не вызывается корректно. Проверьте, что код добавлен в functions.php или в плагин и не вызывает ошибок PHP.
- Отрицательные значения запасов: проверьте настройки управления запасами в WooCommerce (Раздел Товары - Запасы). По умолчанию отрицательные запасы запрещены. Можно добавить проверку в коде, чтобы не уходить в минус.
- Некорректное получение старых данных заказа: иногда данные в базе и объекте заказа могут не совпадать из-за кеширования. Используйте прямые запросы к базе для достоверности.
- Производительность: при большом количестве товаров в заказе выполнение кода может замедлить сохранение. Оптимизируйте запросы и при необходимости делайте обновление асинхронно.
Практические советы по безопасности и производительности
- Используйте
wc_get_product()и методы WooCommerce для работы с товарами, чтобы избежать прямого обновления таблиц без проверки. - Добавьте проверку прав пользователя, если код будет вызываться в админке, чтобы избежать несанкционированных изменений.
- Обрабатывайте исключения и ошибки, чтобы не нарушить работу сайта при сбое в обновлении запасов.
- При большом объёме заказов и товаров рассмотрите возможность кэширования данных и оптимизации SQL-запросов.
- Для сложных сценариев используйте WP-Cron для отложенного обновления запасов без влияния на UX админ-панели.
Сравнение вариантов реализации
| Метод | Плюсы | Минусы |
|---|---|---|
Хук woocommerce_update_order с синхронным обновлением | Простота, мгновенное обновление запасов | Замедляет сохранение заказа при большом объёме данных |
| Отложенное обновление через WP-Cron | Не влияет на скорость админки, масштабируемость | Задержка в обновлении запасов, сложность реализации |
| Использование сторонних плагинов управления запасами | Готовые решения, поддержка сложных сценариев | Стоимость, избыточность, возможные конфликты |