Почему важно отслеживать изменения остатков в WooCommerce
Для интернет-магазинов на WooCommerce контроль остатков товара — ключевой элемент управления запасами. Быстрая реакция на изменение запасов помогает избежать ситуаций с переизбытком или дефицитом товара, а также дает возможность оперативно информировать клиентов и складских сотрудников.
Диагностика проблемы: как понять, что автоматическое отслеживание не настроено
По умолчанию WooCommerce не ведет историю изменений запасов товаров. Это значит, что если вы хотите знать, когда и кем был изменён остаток, а также видеть динамику, нужно внедрять кастомные решения или использовать плагины.
Проверить отсутствие отслеживания можно так:
- Измените остаток товара в админке и проверьте, появляется ли лог с изменениями;
- Проверьте базу данных на наличие таблиц или записей, отвечающих за историю остатков;
- Оцените, есть ли уведомления о снижении остатков.
Пошаговое решение: реализация автоматического логирования изменений запасов
1. Создаем таблицу для хранения логов изменений запасов
function create_stock_log_table() {
global $wpdb;
$table_name = $wpdb->prefix . 'stock_change_log';
$charset_collate = $wpdb->get_charset_collate();
$sql = "CREATE TABLE IF NOT EXISTS $table_name (
id BIGINT(20) NOT NULL AUTO_INCREMENT,
product_id BIGINT(20) NOT NULL,
old_stock INT NOT NULL,
new_stock INT NOT NULL,
changed_by BIGINT(20) DEFAULT NULL,
changed_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id)
) $charset_collate;";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql);
}
register_activation_hook(__FILE__, 'create_stock_log_table');2. Отслеживаем изменения запасов при сохранении товара
Используем хук woocommerce_update_product для фиксации изменений:
add_action('woocommerce_update_product', 'log_stock_changes', 10, 1);
function log_stock_changes($product_id) {
$product = wc_get_product($product_id);
if (!$product) return;
global $wpdb;
$table_name = $wpdb->prefix . 'stock_change_log';
// Получаем старый остаток из мета, если есть
$old_stock = get_post_meta($product_id, '_previous_stock', true);
$new_stock = $product->get_stock_quantity();
// Если старый остаток не установлен, считаем его равным новому
if ($old_stock === '') {
update_post_meta($product_id, '_previous_stock', $new_stock);
return;
}
if (intval($old_stock) !== intval($new_stock)) {
$user_id = get_current_user_id() ?: null;
$wpdb->insert(
$table_name,
[
'product_id' => $product_id,
'old_stock' => intval($old_stock),
'new_stock' => intval($new_stock),
'changed_by' => $user_id,
'changed_at' => current_time('mysql', 1)
],
['%d', '%d', '%d', '%d', '%s']
);
// Обновляем мета для следующего сравнения
update_post_meta($product_id, '_previous_stock', $new_stock);
}
}3. Выводим историю изменений в админке товара
Для удобства добавим метабокс с логами:
add_action('add_meta_boxes', function() {
add_meta_box('stock_change_log_box', 'История изменений остатков', 'render_stock_change_log_box', 'product', 'normal', 'default');
});
function render_stock_change_log_box($post) {
global $wpdb;
$table_name = $wpdb->prefix . 'stock_change_log';
$product_id = $post->ID;
$logs = $wpdb->get_results($wpdb->prepare(
"SELECT * FROM $table_name WHERE product_id = %d ORDER BY changed_at DESC LIMIT 10",
$product_id
));
if (!$logs) {
echo '<p>История изменений отсутствует.</p>';
return;
}
echo '<table style="width:100%; border-collapse: collapse;">';
echo '<thead><tr><th style="border:1px solid #ccc; padding:5px;">Дата и время</th><th style="border:1px solid #ccc; padding:5px;">Старый остаток</th><th style="border:1px solid #ccc; padding:5px;">Новый остаток</th><th style="border:1px solid #ccc; padding:5px;">Изменил</th></tr></thead><tbody>';
foreach ($logs as $log) {
$user_info = $log->changed_by ? get_userdata($log->changed_by) : null;
$user_name = $user_info ? esc_html($user_info->user_login) : 'Система';
echo '<tr>' .
'<td style="border:1px solid #ccc; padding:5px;">' . esc_html($log->changed_at) . '</td>' .
'<td style="border:1px solid #ccc; padding:5px;">' . intval($log->old_stock) . '</td>' .
'<td style="border:1px solid #ccc; padding:5px;">' . intval($log->new_stock) . '</td>' .
'<td style="border:1px solid #ccc; padding:5px;">' . $user_name . '</td>' .
'</tr>';
}
echo '</tbody></table>';
}Проверка результата после внедрения
- Отредактируйте запас товара в админке WooCommerce;
- Обновите страницу редактирования товара и найдите метабокс «История изменений остатков»;
- Проверьте, что добавилась новая запись с правильными значениями старого и нового остатка, а также указано имя пользователя;
- Повторите изменения несколько раз, чтобы убедиться в корректности логирования.
Частые ошибки и как их исправить
- Отсутствие таблицы в базе данных: проверьте, что функция создания таблицы вызвана при активации плагина. Можно вручную вызвать
create_stock_log_table()для теста. - Нет записей в логе: убедитесь, что мета
_previous_stockкорректно обновляется и что хукwoocommerce_update_productсрабатывает. Для проверки можно добавитьerror_log()внутри функции. - Неправильное отображение пользователя: проверьте, что ID пользователя существует и что
get_userdata()возвращает объект. Для автоматических изменений (например, через импорт) может бытьnull.
Практические советы по безопасности и производительности
- Добавьте проверку прав пользователя перед записью лога, чтобы избежать ненужных операций при автоматических процессах.
- Регулярно очищайте устаревшие записи в таблице логов, чтобы не перегружать базу (например, оставлять данные за последние 6 месяцев).
- Для больших магазинов используйте асинхронное логирование через WP-Cron или внешние сервисы, чтобы не замедлять сохранение товара.
Таблица сравнения вариантов реализации
| Метод | Преимущества | Недостатки | Компромисс |
|---|---|---|---|
| Кастомный код (как в статье) | Полный контроль, без плагинов, легкая кастомизация | Требует времени на разработку и тестирование | Подходит для разработчиков, которые хотят гибкость |
| Плагин стороннего разработчика | Быстрое развертывание, поддержка и обновления | Может содержать лишний функционал, нагрузка на сайт | Подходит при нехватке времени и ресурсов |
| Встроенные отчеты WooCommerce (ограниченные) | Нет дополнительной нагрузки, простота | Нет детальной истории изменений запасов | Подходит для базового мониторинга |