
📝 Добавление примечаний к заказу WooCommerce в письмо: полное руководство
Клиент сделал заказ, а вы оставили примечание в админке, уточнили срок доставки, добавили трек-номер или просто написали «позвонить перед отправкой». Но клиент этого не видит. Примечания к заказу WooCommerce по умолчанию живут только в админке, на боковой панели редактирования заказа. В письмах, которые уходят покупателю, их нет.
Проблема решается одним сниппетом в functions.php. Без плагинов, без правки шаблонов, чистый хук, который выводит примечания в тело письма. На практике мы используем этот подход на клиентских магазинах уже несколько лет, ни одного сбоя после обновлений WooCommerce. В этом руководстве разберём, как WooCommerce хранит заметки к заказам, какой хук использовать и как кастомизировать вывод под свои задачи.
💡 Быстрый обзор:
- Как WooCommerce хранит примечания к заказам: тип комментария
order_noteи функцияwc_get_order_notes() - Два подхода к кастомизации писем: переопределение шаблонов или хуки, какой и когда выбирать
- Готовый сниппет для вывода примечаний в письмо о выполнении заказа с разбором каждой строки
- Кастомизация: стилизация списка, фильтрация только клиентских заметок, поддержка plain-text писем
- Тестирование через предпросмотр писем в админке, без спама реальным клиентам
Шаг 1: Как WooCommerce хранит примечания к заказам
WooCommerce хранит примечания как комментарии WordPress типа order_note. Каждое действие с заказом, смена статуса, refund, заметка администратора или сообщение клиенту, записывается в таблицу wp_comments с флагом comment_type = 'order_note'.
Разница между внутренними и клиентскими заметками определяется мета-полем is_customer_note. Если чекбокс «Visible in My Account» включён при добавлении заметки, WooCommerce ставит is_customer_note = true, и покупатель видит её в личном кабинете. Обычные внутренние заметки (is_customer_note = false) клиенту не показываются нигде.
С выходом High Performance Order Storage (HPOS) в WooCommerce 8.2+ заметки попали в отдельную таблицу wp_wc_order_notes. Прямые SQL-запросы к wp_comments с включённым HPOS могут возвращать пустой результат. Поэтому вместо get_comments() лучше использовать wc_get_order_notes(), она работает корректно при любом режиме хранения.
Шаг 2: Шаблоны или хуки, какой подход выбрать
У WooCommerce два пути для кастомизации писем: переопределение шаблонов и использование экшен-хуков. У каждого, свой сценарий.
Переопределение шаблонов. Вы копируете файл из woocommerce/templates/emails/ в yourtheme/woocommerce/emails/ и правите HTML напрямую. Плюс, полный контроль над разметкой. Минус, при крупных обновлениях WooCommerce ваш шаблон может разойтись с актуальной версией, и письма начнут ломаться. Документация по структуре шаблонов теперь на woocommerce.com, а не на старом docs.woothemes.com.
Хуки. Вы вешаете callback на действие woocommerce_email_order_meta, и WooCommerce сам вызывает вашу функцию в нужном месте письма. Плюс, не зависит от версий шаблонов, работает «из коробки» после обновлений ядра. Минус, вы ограничены позицией хука (он срабатывает в блоке мета-данных заказа, после таблицы с товарами).
Критерий | Шаблоны | Хуки |
|---|---|---|
Контроль над разметкой | Полный | В рамках позиции хука |
Устойчивость к обновлениям | Низкая | Высокая |
Сложность внедрения | Выше | Ниже |
Подходит для | Капитальной переделки письма | Добавления одного блока |
Для нашей задачи, вывода примечаний, хука более чем достаточно. Если вам нужно полностью перекроить дизайн писем, берите шаблоны и закладывайте время на их актуализацию раз в полгода.
Шаг 3: Добавляем код в functions.php
Хук woocommerce_email_order_meta срабатывает во всех транзакционных письмах WooCommerce: подтверждение заказа, выполнение, отмена, счёт и другие. В callback передаются четыре параметра: объект заказа $order, флаг $sent_to_admin, флаг $plain_text и объект письма $email.
Разместите этот код в functions.php активной темы (или через плагин Code Snippets, безопаснее, не слетит при смене темы):
1 add_action( 'woocommerce_email_order_meta', 'sd_add_order_notes_to_email', 10, 4 ); 2 3 function sd_add_order_notes_to_email( $order, $sent_to_admin, $plain_text, $email ) { 4 // Не ломаем plain-text версию письма 5 if ( $plain_text ) { 6 echo "\n\n" . esc_html__( 'Order Notes:', 'woocommerce' ) . "\n"; 7 8 $notes = wc_get_order_notes( array( 9 'order_id' => $order->get_id(), 10 'type' => 'customer', 11 ) ); 12 13 if ( empty( $notes ) ) { 14 echo esc_html__( 'No notes for this order.', 'woocommerce' ) . "\n"; 15 return; 16 } 17 18 foreach ( $notes as $note ) { 19 echo '- ' . wp_strip_all_tags( $note->content ) . "\n"; 20 } 21 return; 22 } 23 24 // HTML-версия: получаем только клиентские заметки 25 $notes = wc_get_order_notes( array( 26 'order_id' => $order->get_id(), 27 'type' => 'customer', 28 ) ); 29 30 if ( empty( $notes ) ) { 31 return; 32 } 33 34 echo '<h2>' . esc_html__( 'Order Notes', 'woocommerce' ) . '</h2>'; 35 echo '<ul class="order-notes-list" style="list-style:none;padding:0;margin:0 0 24px;">'; 36 37 foreach ( $notes as $note ) { 38 $note_date = sprintf( 39 '%1$s в %2$s', 40 date_i18n( get_option( 'date_format' ), strtotime( $note->date_created ) ), 41 date_i18n( get_option( 'time_format' ), strtotime( $note->date_created ) ) 42 ); 43 44 printf( 45 '<li style="background:#f9f9f9;border-left:4px solid #7f54b3;padding:12px 16px;margin-bottom:10px;border-radius:0 4px 4px 0;">' 46 . '<div style="margin-bottom:4px;">%s</div>' 47 . '<small style="color:#888;">%s</small>' 48 . '</li>', 49 wp_kses_post( nl2br( $note->content ) ), 50 esc_html( $note_date ) 51 ); 52 } 53 54 echo '</ul>'; 55 }
Что здесь происходит по строкам:
- Строка 1. Регистрируем хук с приоритетом 10 и принимаем все 4 параметра. Без
10, 4WooCommerce передаст только первый аргумент, получите ошибку. - Строки 5-20. Обработка plain-text писем. Некоторые письма уходят в текстовом формате, выводим заметки списком с дефисами, без HTML.
- Строки 24-28.
wc_get_order_notes()запрашивает только клиентские заметки (type => 'customer'). Убрать фильтр, показать все заметки, включая внутренние (смена статуса, refund). Массив пуст → ничего не выводим, письмо не захламляем. - Строки 34-38. Форматируем дату через
date_i18n(), уважает локаль сайта и настройки формата даты/времени. - Строки 40-48.
printf()выводит каждую заметку в стилизованном блоке: фиолетовая полоса слева, светлый фон, дата снизу серым.
Код не использует внешних зависимостей и совместим с WooCommerce 3.2+ (функция wc_get_order_notes появилась именно в этой версии) и HPOS (нативная поддержка с 8.2+).
Шаг 4: Тестируем без спама клиентам
После добавления кода не ждите реальный заказ для проверки. WooCommerce даёт встроенный предпросмотр писем: WooCommerce → Настройки → Emails → нажмите на любое письмо → кнопка «Preview» внизу. Вы увидите письмо в HTML и plain-text, с настоящим заказом (берётся последний существующий). Если заказов нет, создайте тестовый вручную.
Что проверить:
- Клиентская заметка с включённым «Visible in My Account» отображается в письме;
- Внутренняя заметка (без галки) не отображается (если оставили
type => 'customer'); - Plain-text версия письма не сломана (переключитесь по ссылке под предпросмотром);
- Блок с заметками не дублируется в письмах администратору (добавьте проверку
if ( $sent_to_admin ) return;если нужно).
Если пользуетесь Code Snippets, достаточно деактивировать сниппет для отката. Если правили functions.php, закомментируйте или удалите код. Никаких следов в базе не остаётся.
⁉️🤔 Частые вопросы
Можно ли выводить ВСЕ примечания, а не только клиентские?
Да. Замените
'type' => 'customer'на'type' => 'internal', получите только внутренние заметки. Уберите параметрtypeсовсем, получите все заметки подряд. Учитывайте, что внутренние заметки могут содержать служебную информацию (ID refund, суммы), которую клиенту видеть ни к чему. Большинству магазинов достаточно'type' => 'customer'. Если хотите разделить вывод, сделайте два блока: сначала клиентские заметки с заголовком «Комментарии к заказу», затем внутренние под заголовком «Детали выполнения» (это уже для писем администратору, флаг$sent_to_admin).
Код работает для конкретного письма или для всех сразу?
Хук
woocommerce_email_order_metaсрабатывает во всех транзакционных письмах: Completed Order, Processing Order, Order on Hold, Customer Invoice и так далее. Если нужно ограничить вывод только письмом о выполнении заказа, оберните код в проверку:
1 > if ( 'customer_completed_order' !== $email->id ) { 2 > return; 3 > } 4 > ```
1
plaintext
1 2 plaintext 3 4 Список всех ID писем доступен в документации WooCommerce: `customer_completed_order` · `customer_processing_order` · `customer_on_hold_order` · `customer_invoice` · `customer_refunded_order` · `customer_new_account` · `new_order` · `cancelled_order` · `failed_order`. 5 6 **Безопасно ли править functions.php на боевом сайте?** 7 8 > Правка `functions.php` напрямую, рискованный путь. Ошибка в синтаксисе (лишняя скобка, незакрытая кавычка) кладёт весь сайт в белый экран. Безопаснее использовать плагин Code Snippets, он позволяет добавлять PHP-сниппеты через админку и автоматически отключает код с фатальной ошибкой. Альтернатива: WPCode (бесплатная версия на wordpress.org), тот же функционал плюс условная логика (только для админов, только на определённых страницах). Если всё же правите `functions.php`, сделайте бэкап файла и держите под рукой FTP-доступ. Белый экран лечится заменой `functions.php` на оригинал из бэкапа. 9 10 **Примечания дублируются в письмах, что я сделал не так?** 11 12 > Две вероятные причины. Первая: вы одновременно добавили код и в `functions.php`, и через плагин сниппетов, хук срабатывает дважды. Оставьте что-то одно. Вторая: тема или другой плагин уже вешают callback на этот же хук. Проверьте через поиск по коду проекта (`grep -r "woocommerce_email_order_meta" wp-content/`). Если дублирование идёт от плагина, достаточно убрать ваш код. Если от темы, оберните свой callback в `remove_action()` перед `add_action()`, чтобы гарантированно выполнялся только один обработчик. 13 14 ## Что в итоге: стоит ли добавлять примечания в письма клиентам 15 16 Прозрачность коммуникации, один из самых дешёвых способов снизить нагрузку на поддержку. Когда клиент видит в письме «Трек-номер: X123456789, доставка 15 июня», он не идёт писать в чат «а где мой заказ?». Один сниппет, ноль плагинов, пять минут на внедрение, а эффект измеряется десятками сэкономленных тикетов в месяц. 17 18 Если письма уже кастомизированы через шаблоны, встройте вывод заметки прямо в шаблон, а не хук. Если пользуетесь плагином кастомизации писем (Kadence, YayMail, ShopMagic), у каждого из них есть свой механизм добавления динамических блоков, используйте его вместо кода. 19 20 Попробуйте с одним типом писем (Completed Order), проверьте на тестовом заказе и через неделю оцените, стало ли меньше однотипных вопросов от клиентов. [Почти](/recomendet-startmail_com) наверняка, да.
plaintext
1



