Диагностика проблемы: зачем запрещать изменение цен после оформления заказа
В WooCommerce цена товара может изменяться в админке или программно, но если заказ уже оформлен, изменение цены товара задним числом приводит к разногласиям в учёте, отчетах и конфликтам с бухгалтерией. Часто это случается, если цена меняется через массовое обновление или автоматическую синхронизацию с внешним сервисом.
Проверить, есть ли проблема, можно в административном разделе «Заказы»: сравните цену товара в заказе и текущую цену товара в каталоге. Если они отличаются, это потенциальная проблема.
Пошаговое решение: блокируем изменения цены товара после оформления заказов
1. Копируем текущую цену в метаполе при оформлении заказа
Для сохранения цены, по которой товар был куплен, при создании заказа используем хук woocommerce_checkout_create_order_line_item:
add_action('woocommerce_checkout_create_order_line_item', 'save_product_price_in_order_item_meta', 10, 4);
function save_product_price_in_order_item_meta($item, $cart_item_key, $values, $order) {
$product = $item->get_product();
if ($product) {
$price = $product->get_price();
$item->update_meta_data('_original_product_price', $price);
}
}2. Запрещаем изменение цены товара в админке после оформления заказов
Чтобы избежать изменения цены товара, если на него есть заказы, запретим редактирование цены в админке. Для этого используем фильтр 'woocommerce_product_data_tabs' и проверяем наличие заказов с этим товаром.
add_filter('woocommerce_product_data_tabs', 'disable_price_edit_if_orders_exist', 10, 1);
function disable_price_edit_if_orders_exist($tabs) {
global $post;
if (!$post) return $tabs;
$product_id = $post->ID;
$orders = wc_get_orders(array(
'limit' => 1,
'status' => array('processing', 'completed', 'on-hold'),
'product_id' => $product_id
));
if (!empty($orders)) {
if (isset($tabs['general'])) {
unset($tabs['general']['fields']['_regular_price']);
unset($tabs['general']['fields']['_sale_price']);
// Если не сработает, можно скрыть через JS или CSS
}
}
return $tabs;
}Однако WooCommerce не предоставляет прямого способа убрать поля цены через фильтр, поэтому дополнительно можно отключить возможность сохранения цен через хук save_post_product:
add_action('save_post_product', 'prevent_price_update_if_orders_exist', 10, 3);
function prevent_price_update_if_orders_exist($post_ID, $post, $update) {
if (!$update) return;
$orders = wc_get_orders(array(
'limit' => 1,
'status' => array('processing', 'completed', 'on-hold'),
'product_id' => $post_ID
));
if (!empty($orders)) {
// Запретим обновление цены
remove_action('save_post_product', 'woocommerce_process_product_meta');
// Либо перезапишем цену старой
$old_product = wc_get_product($post_ID);
if ($old_product) {
update_post_meta($post_ID, '_regular_price', $old_product->get_regular_price());
update_post_meta($post_ID, '_price', $old_product->get_price());
}
}
}3. Обновляем цену в заказах только в исключительных случаях
Если по бизнес-логике нужно менять цену в уже оформленных заказах, делайте это вручную и только с согласия бухгалтерии, иначе лучше полностью запретить.
Проверка результата
- Оформите тестовый заказ с товаром.
- Попробуйте изменить цену товара в админке — изменение должно не сохраниться или быть заблокировано.
- Проверьте, что в заказе сохранена оригинальная цена в метаполе
_original_product_price. - Запустите отчеты WooCommerce, чтобы убедиться, что данные по заказам не изменились.
Частые ошибки и как их исправить
- Цена меняется в базе, но не в заказах: это штатное поведение, цена в заказе сохранена отдельно, поэтому обновлять цену товара нельзя задним числом.
- Поля цены не скрываются в админке: WooCommerce не всегда позволяет убрать стандартные поля через фильтры, используйте CSS или JavaScript в админке для скрытия.
- Функция блокировки сохранения цены не срабатывает: убедитесь, что код подключен в плагине или functions.php и что нет конфликтов с другими плагинами, которые тоже изменяют цены.
Практические советы по безопасности и производительности
- Для проверки наличия заказов используйте параметры с лимитом 1, чтобы не загружать лишние данные.
- Кешируйте результаты запросов наличия заказов, если эта проверка вызывается часто.
- Храните оригинальную цену товара в метаполе заказа — это предотвратит ошибки при анализе данных.
- Внедряйте проверку и блокировку изменения цены на уровне бизнес-логики, а не только на уровне интерфейса.
Сравнение подходов: плагин vs. код vs. компромисс
| Подход | Плюсы | Минусы | Компромисс |
|---|---|---|---|
| Плагин (например, «Prevent Price Change After Order») | Простота установки, поддержка, тестирование | Может быть платным, нагрузка на сайт | Использовать проверенные плагины с хорошими отзывами |
| Код в functions.php или плагине | Полный контроль, легковесность, гибкость | Нужны навыки разработки, поддержка вручную | Писать чистый и оптимизированный код, тестировать на тестовом сайте |
| Компромисс (частичное ограничение, уведомление админа) | Гибкость, информирование о рисках | Не полная защита, возможны ошибки | Использовать уведомления с возможностью отмены изменений |