Диагностика проблемы: почему нужно запретить удаление товаров после продажи
В WooCommerce стандартно отсутствует ограничение на удаление товаров из админки, вне зависимости от того, были ли они проданы. Это может привести к потере данных о продажах, нарушению отчетности и проблемам с налоговой документацией. Особенно важно для магазинов с большим объемом заказов сохранить историю проданных товаров.
Пошаговое решение: запрет удаления товаров с заказами в WooCommerce
1. Проверка, был ли товар продан
Для начала нужно написать функцию, которая проверит, есть ли у товара завершённые заказы. Проверка будет происходить по статусу заказов completed или processing.
function has_product_sales($product_id) {
global $wpdb;
$query = $wpdb->prepare(
"SELECT COUNT(*) FROM {$wpdb->prefix}woocommerce_order_items AS order_items
INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS itemmeta ON order_items.order_item_id = itemmeta.order_item_id
INNER JOIN {$wpdb->prefix}posts AS posts ON order_items.order_id = posts.ID
WHERE posts.post_type = 'shop_order'
AND posts.post_status IN ('wc-completed','wc-processing')
AND itemmeta.meta_key = '_product_id'
AND itemmeta.meta_value = %d",
$product_id
);
$count = $wpdb->get_var($query);
return $count > 0;
}2. Запрет удаления товара через фильтр WordPress
Используем хук user_has_cap, чтобы снять право удаления товара, если он был продан.
add_filter('user_has_cap', function($allcaps, $caps, $args, $user) {
if (isset($args[0]) && in_array($args[0], ['delete_post', 'delete_product'])) {
$post_id = $args[2];
$post_type = get_post_type($post_id);
if ($post_type === 'product' && has_product_sales($post_id)) {
$allcaps['delete_post'] = false;
$allcaps['delete_product'] = false;
}
}
return $allcaps;
}, 10, 4);3. Блокировка удаления через Ajax и Bulk Actions
WooCommerce и WordPress позволяют удалять товары через массовые действия и AJAX-запросы. Для надежной защиты добавим проверку на уровне pre_delete_post.
add_action('pre_delete_post', function($post_id) {
if (get_post_type($post_id) === 'product' && has_product_sales($post_id)) {
wp_die('Удаление товара, который был продан, запрещено.');
}
});Проверка результата после внедрения
- Попробуйте удалить товар, который не имел продаж — удаление должно пройти.
- Попробуйте удалить товар, который продавался — должно появиться сообщение об ошибке и удаление не произойдет.
- Проверьте массовое удаление (Bulk Actions) в списке товаров — товары с продажами не должны удаляться.
Частые ошибки и как их исправить
- Ошибка: Товары с продажами удаляются.
Причина: Кеширование ролей и прав в WordPress.
Решение: После добавления фильтра сбросьте кеш прав пользователей с помощьюwp_cache_flush()или повторного входа в админку. - Ошибка: Сообщение об ошибке не отображается при удалении через Bulk Actions.
Причина: Ajax-запросы не всегда выводятwp_die()корректно.
Решение: Для полного контроля создайте дополнительный фильтр дляhandle_bulk_actions-edit-productи возвращайте ошибку там. - Ошибка: Удаление не блокируется для пользовательских ролей.
Причина: Фильтрuser_has_capможет не охватывать все права.
Решение: Проверьте, что все роли, имеющие право удалять товары, корректно обрабатываются фильтром.
Практические советы по безопасности и производительности
- Оптимизация запроса: Используйте индексы на таблицах
woocommerce_order_itemsиwoocommerce_order_itemmetaдля ускорения проверки продаж. - Кеширование результатов: При больших магазинах кешируйте результат
has_product_sales()через Transients или объектный кеш, чтобы не нагружать базу при каждом запросе. - Журналирование попыток удаления: Для безопасности можно добавить логирование попыток удаления товаров с продажами через
error_log()или сторонние решения. - Тестирование на staging-сервере: Перед внедрением на живом сайте обязательно проверяйте совместимость с установленными плагинами и темой.
Сравнение вариантов реализации блокировки удаления товаров с продажами
| Метод | Плюсы | Минусы | Пример |
|---|---|---|---|
Фильтр user_has_cap | Блокирует удаление на уровне прав пользователя, предотвращает появление кнопки удаления | Не всегда блокирует AJAX и Bulk Actions полностью | Второй код из статьи |
Хук pre_delete_post | Останавливает удаление в любом случае, включая AJAX и массовые действия | Сообщение об ошибке не всегда красиво отображается | Третий код из статьи |
| Плагин для контроля прав (например, User Role Editor) | Удобный UI для настройки прав | Не решает логику проверки продаж, нужна доп. кастомизация | Необходимо доработать |