Массовое редактирование каталога: 10 000 карточек без выгрузки в Excel
Классический цикл правки цен — экспорт в CSV, редактирование в Excel, импорт обратно — теряет мета-поля и убивает пятницу. Показываю, как табличный редактор прямо в админке WooCommerce закрывает массовую наценку, смену категорий и SEO-полей без единой выгрузки.
COS / KNOWLEDGE BASE
# Массовое редактирование каталога: 10 000 карточек без выгрузки в Excel
Мне однажды прислали счёт за работу, которую формально нельзя назвать разработкой — это была просто ручная правка цен. Владелец магазина строительных материалов нанял фрилансера, чтобы поднять цены на девять процентов по одной крупной товарной группе перед сезоном, и отдельно поправить категории у нескольких сотен позиций, которые за зиму переехали из одного раздела каталога в другой. Фрилансер выставил счёт на сорок два часа работы. Не сорок два часа разработки нового функционала, не сорок два часа дизайна — сорок два часа того, что технически называется «открыть каждую карточку товара, поменять цифру в поле цены, сохранить, перейти к следующей». По ставке в полторы тысячи рублей в час это вышло в шестьдесят три тысячи рублей за операцию, которая с точки зрения здравого смысла должна занимать минуты, а не рабочую неделю.
Владелец потом рассказывал мне эту историю с усталой усмешкой — говорил, что и сам понимал абсурдность ситуации в процессе, но не видел альтернативы: штатного разработчика в компании нет, а объяснять фрилансеру, как настроить массовый импорт через сторонний плагин так, чтобы не потерять половину карточек, оказалось дороже и дольше, чем просто заплатить за ручной труд. Он же признался, что подобное происходит у него не первый раз — предыдущая ценовая ревизия полгода назад стоила примерно столько же, просто счёт выставлял другой человек, и цифра забылась. Регулярность этой боли меня и зацепила: это не разовый форс-мажор, а встроенная в бизнес-процесс статья расходов, которую просто никто не считает как отдельную строку, потому что она размазана по разным подрядчикам и разным месяцам.
Я посмотрел на этот счёт и понял, что дело не в жадности фрилансера и не в его медлительности — он просто честно потратил время на единственный доступный ему способ массового изменения каталога WooCommerce: карточка за карточкой, вручную. Стандартная админка WordPress для этого не предназначена в принципе — WooCommerce даёт быстрое редактирование buit-in только для пары полей вроде цены и статуса, да и то через довольно неуклюжий интерфейс, который не масштабируется дальше пары десятков товаров одновременно. А дальше начинается то, что я называю «ритуал Excel»: экспортируем каталог в CSV, открываем в Excel, правим руками или формулами, сохраняем, импортируем обратно — и молимся, чтобы ничего не потерялось и не съехало.
Я видел этот ритуал десятки раз на самых разных магазинах, и почти каждый раз он заканчивался одинаково: что-то ломается. Причём страдает не обязательно тот, кто вносил правку, — чаще всего проблему обнаруживает кто-то третий, спустя дни или недели, когда уже сложно вспомнить, какое именно действие её вызвало, и приходится реконструировать причину по обрывочным следам в логах и переписке. То Excel меняет формат ячейки с ценой и вместо «1200.50» после сохранения там оказывается «1 200,50» с неразрывным пробелом, который WooCommerce не понимает и трактует как ноль. То кодировка CSV при экспорте была UTF-8, а при повторном сохранении в Excel на Windows превратилась в Windows-1251, и все кириллические названия категорий превратились в вопросительные знаки. То при импорте плагин, который делает эту операцию, аккуратно обновил поле цены, но заодно стёр все кастомные мета-поля, потому что его логика импорта построена на принципе «полная замена записи», а не «точечное обновление указанных столбцов». Я расскажу, как мы в COS WP Woo решили эту проблему — не ещё одним CSV-импортёром, а полноценным табличным редактором внутри админки, который не заставляет данные покидать WordPress вообще.
Ритуал Excel — почему экспорт-импорт не масштабируется
Чтобы понять, зачем вообще нужен отдельный инструмент массового редактирования, стоит разобрать по косточкам, что именно идёт не так в классическом цикле «выгрузил — поправил — загрузил». Начнём с самого безобидного на вид шага — экспорта. WooCommerce из коробки такого экспорта не делает вовсе, а сторонние экспортёры обычно вываливают в CSV огромное количество столбцов, большая часть которых вам не нужна для конкретной задачи. Хотите поднять цену на одну товарную группу — получаете файл с полусотней колонок, включая технические ID вариаций, идентификаторы галерей изображений, сериализованные атрибуты. В этом файле легко запутаться, легко случайно тронуть не ту колонку, и уже на этом этапе повышается риск ошибки.
Есть и организационная сторона проблемы, которую редко обсуждают, но которая на практике съедает не меньше времени, чем технические сбои. Экспорт-импорт — это всегда файл, который живёт где-то отдельно от системы: в почте, в общей папке, в мессенджере. Пока один сотрудник правит скачанную копию, второй может параллельно зайти в админку и поменять тот же товар вручную — и когда файл наконец импортируется обратно, его правки тихо перезатирают то, что второй человек успел сделать за это время. Никакого предупреждения о конфликте версий здесь не предусмотрено в принципе, потому что сама архитектура «файл в отрыве от системы» не подразумевает понятия одновременного доступа, которое есть у любой нормальной многопользовательской системы.
Дальше — сам процесс редактирования в Excel. Казалось бы, что может пойти не так в табличном редакторе, созданном именно для табличных данных? Но Excel — это программа общего назначения, которая пытается угадать формат данных, и эти угадывания регулярно ломают то, что для WooCommerce критично. Артикул «00123» превращается в число «123» и теряет ведущие нули. Дата начала акции меняет формат в зависимости от региональных настроек операционной системы того, кто открывает файл. Ячейка с длинным описанием, содержащим переносы строк или HTML-теги, может побиться при сохранении, если Excel решит, что это несколько строк, а не одна. А если файл открывает не тот человек, который его создавал, — региональные настройки могут не совпадать, и то, что на одном компьютере выглядело правильно, на другом читается совершенно иначе.
И вот последний, самый болезненный этап — обратный импорт. Здесь начинается настоящая лотерея, потому что логика большинства импортёров построена вокруг идентификации товара по ID или по SKU, и дальше — либо полная перезапись найденной записи данными из файла, либо построчное сопоставление колонок, где один неверно сопоставленный столбец может затереть совсем не то поле, которое вы хотели изменить. Я лично видел ситуацию, когда при импорте «только цен» плагин заодно перезаписал поле short_description пустой строкой, потому что в CSV эта колонка присутствовала, но была пустой — а плагин интерпретировал пустое значение как «нужно очистить», а не «оставить как есть». Тысяча товаров молча потеряли короткое описание, которое участвовало в сниппетах на страницах категорий. Заметили это только через две недели, когда SEO-специалист начал разбираться, почему просела кликабельность в выдаче.
Похожая история с датами акций — если в исходном файле дата стояла в формате «день.месяц.год», а сохранял правки коллега с американской локалью Windows, при повторном открытии в Excel дата могла тихо перевернуться местами день и месяц, и вместо акции с первого по пятое число магазин с равными шансами получал акцию с пятого по первое, что WooCommerce интерпретирует как уже закончившуюся или ещё не начавшуюся распродажу в зависимости от текущей даты.
Отдельно скажу про артикулы, потому что это боль, с которой сталкивался лично раз десять на разных магазинах. Артикул «00458» после открытия в Excel без явного указания текстового формата колонки превращается в число «458» — ведущие нули отваливаются молча, без единого предупреждения. Если структура ваших артикулов строится на фиксированной длине с нулями впереди — а так делает огромное количество производителей и дистрибьюторов, — после такого «безобидного» открытия файла в Excel сверка с прайс-листом поставщика или с накладной 1С попросту перестаёт совпадать, потому что «458» и «00458» для системы сопоставления — это разные строки. Обнаруживается это обычно не сразу, а в момент следующей синхронизации, когда десятки товаров вдруг перестают сопоставляться с источником и создаются заново как дубли.
Есть и более фундаментальная проблема с этим циклом — он не даёт предпросмотра результата до момента фактического применения. Вы либо доверяете файлу и жмёте «импортировать», надеясь, что сопоставление столбцов настроено верно, либо тестируете на подмножестве товаров, что удлиняет и без того долгий процесс. И если что-то пошло не так — откатывать приходится либо из бэкапа базы данных целиком, что я подробно разбирал в статье про бэкап и восстановление магазина, либо вручную, товар за товаром, вспоминая, что было «до». Ни то ни другое не назовёшь быстрым решением проблемы, которая изначально возникла из желания сделать что-то быстро.
Таблица прямо в админке — фильтруем, видим, правим
Когда мы проектировали модуль массового редактирования, отправной точкой была простая мысль: данные не должны покидать WordPress ради того, чтобы их отредактировать. Всё должно происходить там же, где товар живёт — в базе данных WooCommerce, через официальные сеттеры объекта товара, с сохранением всех хуков и валидации, которые WooCommerce ожидает при изменении данных. Результат — интерфейс, похожий на электронную таблицу, но работающий поверх реального каталога в реальном времени.
Первое, с чего начинается работа — это фильтрация. И здесь я настаивал на том, чтобы фильтры были действительно гибкими, а не «покажи товары категории X». В модуле можно фильтровать практически по любому полю или мета-данным: по названию, по описанию (в том числе — есть ли оно вообще, что для контентного аудита каталога само по себе ценная функция), по SKU, по цене и диапазону цен, по остаткам и статусу наличия, по весу и габаритам, по категориям и тегам, по конкретным атрибутам с конкретными значениями, по классу доставки, по видимости в каталоге, по признаку «рекомендуемый товар». Операторы фильтрации при этом не ограничены банальным «равно» — есть больше и меньше, диапазон между двумя значениями, вхождение в список, точное совпадение и его отрицание, а отдельная пара операторов «пусто» и «не пусто» закрывает как раз тот самый сценарий контентного аудита: покажи мне все товары, у которых пустое описание, или нет главного изображения, или не проставлен вес, из-за чего служба доставки не может рассчитать стоимость.
Отдельно стоит сказать про фильтрацию по атрибутам, потому что для магазинов с технической спецификой — те же смазочные материалы, крепёж, кабельная продукция — это едва ли не более востребованный сценарий, чем фильтрация по категории. Атрибут «Вязкость» со значением «5W-30», атрибут «Класс допуска» со значением конкретного стандарта, атрибут «Диаметр» с конкретным числом в миллиметрах — всё это доступно как отдельное условие фильтра, причём можно комбинировать несколько атрибутов одновременно через логическое И или ИЛИ. Практический пример: нужно поднять цену на все моторные масла определённой вязкости от определённого бренда, которые при этом ещё и не были обновлены дольше полугода, — собирается фильтр из трёх условий, атрибут вязкости, атрибут бренда и дата изменения, и результат сразу виден в таблице до применения любой операции.
Отдельно у нас реализована настройка видимых столбцов таблицы — вы сохраняете профиль колонок под конкретную задачу: «профиль для ценовой ревизии» показывает только название, артикул, обычную и акционную цену, а «профиль для контентного аудита» выводит на первый план длину описания в словах, наличие изображения, число фотографий в галерее. Переключение между профилями занимает секунду и избавляет от необходимости каждый раз заново прятать десяток ненужных для текущей задачи столбцов — мелочь, но именно из таких мелочей складывается ощущение, что инструментом приятно пользоваться каждый день, а не только когда совсем прижало.
Ради последнего сценария у нас есть отдельный набор «умных фильтров» — это заранее собранные типовые проблемы каталога, оформленные в один клик: товары без описания, без изображения, с нулевой ценой, не в наличии, черновики, без категории, без артикула. Причём рядом с каждым умным фильтром сразу показывается число товаров, которые под него попадают, и это число считается не абстрактно, а прямо по текущему состоянию базы — открыл вкладку и сразу видишь, условно, «247 товаров без изображения», без необходимости запускать отдельный отчёт или писать SQL-запрос руками. Для магазина, который годами наполнялся разными людьми в разном темпе, это часто становится неприятным, но полезным открытием: оказывается, четверть каталога тихо стоит без нормального описания, и никто об этом не подозревал, пока не увидел цифру.
После того как выборка сформирована, начинается собственно редактирование — и здесь ключевое слово inline, то есть прямо в ячейке таблицы, без перехода на отдельную страницу редактирования товара. Кликнул на цену — она стала редактируемым полем, ввёл новое значение, сохранил, ячейка визуально подтвердила изменение. То же самое с наличием, с категорией, с featured-флагом. Для вариативных товаров реализован отдельный уровень — можно раскрыть родительский товар и увидеть список вариаций с теми же возможностями редактирования, что критично для каталогов, где один продукт продаётся в нескольких фасовках или размерах и у каждой вариации своя цена и свой остаток. Отдельно скажу про производительность, потому что для каталога в десять-пятнадцать тысяч товаров это не праздный вопрос. Подсчёт количества товаров по умным фильтрам кэшируется на пять минут — этого достаточно, чтобы открыть вкладку фильтров и мгновенно увидеть актуальные цифры, не пересчитывая тяжёлые запросы по всей базе при каждом клике, и в то же время достаточно свежо, чтобы не показывать данные многочасовой давности. Сама таблица в интерфейсе запрашивает данные постранично, не больше сотни строк за раз, чтобы браузер не захлёбывался от рендеринга огромного DOM при выборке в тысячи позиций, а для операций «применить ко всем товарам по фильтру» система резолвит полный список ID без пагинации отдельным лёгким запросом и обрабатывает уже сам список пачками — та же логика чанкования, что я подробно описывал в статье про бэкапы, применительно уже к операциям над каталогом.
Сортировка при этом работает по девяти полям — от даты создания и заголовка до цены, артикула, остатка, порядка отображения, даты изменения, количества продаж и внутреннего ID, — что позволяет, например, отсортировать выборку по количеству продаж и в первую очередь разобраться с бестселлерами, у которых обнаружилась проблема с ценой, а не тратить время на позиции, которые всё равно никто не покупает.
Массовые операции — наценка, замена, формулы
Инлайн-редактирование ячейка за ячейкой решает точечные правки, но исходная боль — это именно массовая операция над выборкой в сотни и тысячи товаров одновременно, и здесь начинается самая интересная часть модуля. Вы формируете фильтр — скажем, категория «Кабельная продукция» — выбираете «применить ко всем товарам по фильтру» (а не только к тем, что видны на текущей странице таблицы), и дальше решаете, что именно с ними сделать.
Прежде чем перейти к конкретным типам операций, скажу пару слов про саму механику применения — это тоже не мелочь. Операция запускается не над видимой на экране страницей из двадцати пяти строк, а над полным списком идентификаторов, который система сначала резолвит по вашему фильтру без ограничения пагинацией, а затем обрабатывает пачками. Это значит, что фраза «применить ко всем товарам по фильтру» не является преувеличением или маркетинговой формулировкой — она в буквальном смысле затрагивает каждый товар, который подходит под условие, будь их двадцать или двадцать тысяч, и вы можете быть уверены, что ни один подходящий товар не выпадет из выборки просто потому, что не поместился на первую страницу таблицы.
Для числовых полей — цены, остатка, веса, габаритов, порядка сортировки — доступен целый набор операций, а не только банальное «поставить новое значение». Можно прибавить фиксированное число, можно вычесть, можно увеличить на процент, можно уменьшить на процент — и именно это чаще всего нужно бизнесу, потому что реальная жизнь редко требует «поставить всем одинаковую цену», а почти всегда требует «поднять цену на девять процентов от текущей», причём у разных товаров текущая цена разная. Отдельно есть операция «формула», которая раскрывает по-настоящему гибкий сценарий: вы пишете математическое выражение вроде `regular_price * 1.1 + 50`, и система вычисляет результат индивидуально для каждого товара, подставляя вместо переменных его собственные значения — цену, цену по акции, остаток, вес, габариты, порядок сортировки. Это закрывает кейсы вроде «поднять цену на десять процентов, но затем добавить фиксированную наценку за упаковку» одним действием на всю выборку, вместо мучительного пересчёта в Excel с последующим импортом.
Формула — не игрушка ради галочки в списке возможностей, а рабочий инструмент, которым закрываются реальные ценовые кампании. Один клиент рассчитывал розничную цену как «себестоимость плюс тридцать пять процентов маржи, минус пять рублей за счёт партнёрской скидки поставщика, округлить до целого» — три отдельных условия, которые в Excel потребовали бы вложенных формул, аккуратного протягивания на весь диапазон и постоянного риска съехать на строку при копировании. Здесь это одна строка формулы, применённая единой операцией к выборке в три с половиной тысячи позиций, с округлением до нужного количества знаков после запятой, которое тоже задаётся отдельным параметром операции. Ещё один частый сценарий — сезонная наценка на габаритные товары, где формула учитывает не только цену, но и вес: тяжёлые позиции получают чуть больший процент наценки, потому что их дороже возить, и это тоже укладывается в одну формулу с переменной веса товара.
Для текстовых полей — названия, описания, короткого описания, артикула, примечания к покупке — логика другая: можно полностью заменить значение, можно дописать текст в конец существующего (например, добавить приписку «— новая партия» ко всем товарам определённого поставщика), можно дописать в начало, и есть операция найти-заменить, которая ищет подстроку в текущем значении и меняет её на другую — это спасает, когда у полутысячи товаров в описании фигурирует старое название бренда после ребрендинга, а переписывать вручную нет никакого желания. Для булевых полей — рекомендуемый товар, виртуальный, скачиваемый, ведётся ли учёт остатков — можно либо явно установить значение, либо переключить его на противоположное у всей выборки разом. Для категорий и тегов реализованы операции добавления, удаления и полной замены — то есть можно как добавить новую категорию ко всем товарам из выборки, сохранив существующие, так и полностью пересадить выборку в другую ветку каталога.
Прежде чем что-либо реально сохранится в базе, система показывает предпросмотр — по умолчанию на выборке из двадцати товаров показывает, каким было значение и каким станет после применения операции. Это тот самый шаг, которого фатально не хватает классическому циклу с Excel: вы видите результат до того, как он необратимо запишется в тысячи карточек, и можете поймать ошибку в логике — например, обнаружить, что формула наценки даёт отрицательную цену для товаров, которые уже были на распродаже, прежде чем это разъедет весь каталог. Кстати, о защите от подобных нелепостей: система на уровне валидации не даёт цене или остатку уйти в отрицательные значения, а для связки цены и цены по акции есть отдельная проверка целостности — акционная цена не может оказаться выше или равна обычной цене, и если формула или массовая операция даёт именно такой результат, применение по этому конкретному товару блокируется с понятной причиной в отчёте, а не тихо создаёт нелогичную карточку, где скидочная цена выше обычной.
Откат и история — что делать, если наценку применили не к той выборке
Здесь я подхожу к функции, ради которой, будь моя воля, я бы переписал заново весь класс «инструментов массового редактирования» на рынке — потому что почти никто не относится к откату всерьёз, а зря. Каждая массовая операция в системе получает уникальный идентификатор пакета, и перед тем как записать новое значение, система сохраняет в отдельную таблицу истории старое значение для каждого затронутого товара. Не просто факт «операция была выполнена», а конкретное состояние поля у конкретного товара до изменения. Это значит, что если вы применили наценку в двенадцать процентов не к той категории — а такое, поверьте, случается даже у опытных людей, особенно вечером в пятницу, когда фильтр забыли сбросить с предыдущей задачи, — операция откатывается одним действием: система проходит по сохранённой истории и построчно, пакетами по пятьдесят товаров за раз, чтобы не перегружать сервер на большой выборке, возвращает именно то значение, которое было записано в момент до операции.
История при этом хранит не только сам факт изменения, но и метаданные вокруг него — какое поле менялось, какой тип операции применялся, какой пользователь это делал, сколько товаров было затронуто, была ли операция уже отменена ранее. Это превращает раздел истории в полноценный журнал аудита изменений каталога: если через неделю кто-то спрашивает «а кто вообще поднял цены на этой неделе и на сколько», ответ находится за десять секунд поиска по логу, а не через раскопки в переписке с сотрудниками. Отдельно система защищает от двойного отката — если пакет уже помечен как отменённый, повторная попытка отмены корректно возвращает ошибку, а не откатывает откат, что могло бы создать путаницу с реальным текущим состоянием цен.
Расскажу конкретный пример того, как этот механизм спасает вечер. У одного клиента менеджер, готовя сезонное снижение цен на летний ассортимент, забыл сбросить фильтр после предыдущей задачи — фильтр остался настроен не на «летние товары», а на «все товары дороже тысячи рублей», что после предыдущей выборки просто не сбросилось само. Операция «снизить цену на пятнадцать процентов» ушла не на триста позиций летнего ассортимента, а почти на две тысячи товаров всего каталога, включая зимние позиции и премиальные товары, которые снижать никто не планировал. Обнаружили это через сорок минут, когда начали поступать нетипично дешёвые заказы на товары, которые обычно так не покупают. Паники не случилось ровно по одной причине — операция была одним пакетом с известным идентификатором, и откат вернул исходные цены всем двум тысячам товаров за несколько секунд, после чего операцию повторили уже с правильно настроенным фильтром. Без истории и отката это был бы вечер ручного восстановления цен по прайс-листу или разворачивание бэкапа базы данных с потерей всех заказов, оформленных за эти сорок минут.
Есть нюанс, о котором стоит сказать честно: откат возвращает значение конкретного поля, но не воскрешает товар, если после массовой операции с ним произошло что-то ещё — скажем, вы откатили наценку, но параллельно кто-то вручную поменял этот же товар по другой причине, и это более позднее изменение откатом не затрагивается, потому что откат работает по принципу «вернуть именно то поле к именно тому значению», а не «отмотать всю историю товара назад во времени». Это осознанное ограничение: полная машина времени по каждому товару потребовала бы версионирования всего объекта целиком, что excessive для большинства реальных сценариев, а точечный откат конкретной массовой ошибки закрывает девяносто пять процентов случаев, ради которых люди вообще ищут функцию отмены.
Для операций, которые вы выполняете регулярно — скажем, ежемесячная сезонная наценка на определённую категорию перед праздниками — есть пресеты: сохраняете один раз связку «фильтр плюс операция плюс параметры» под понятным названием и в следующий раз просто запускаете пресет одной кнопкой вместо того, чтобы заново собирать фильтр и вспоминать точную формулу. Это, кстати, снимает ещё один класс ошибок — человеческий фактор при повторном ручном вводе одних и тех же параметров месяц за месяцем. У части клиентов набралась целая библиотека таких пресетов под разные регулярные задачи — «плановая наценка перед НГ», «снять с публикации сезонные позиции», «проставить статус on backorder для поставки под заказ» — и раз в месяц ответственный сотрудник просто прогоняет нужный пресет вместо того, чтобы вспоминать логику операции заново.
Отдельно скажу, что раздел истории — это не только инструмент отката, но и полноценный фильтруемый журнал: можно искать записи по конкретному полю, по типу операции, по конкретному сотруднику, по диапазону дат, по статусу «уже отменено / ещё в силе». На практике это чаще используется не для отката, а для банальной сверки: маркетолог поднимал цены на прошлой неделе, бухгалтерия хочет свериться, что именно и когда менялось, — открывается история, выставляется фильтр по имени сотрудника и диапазону дат, и весь список изменений перед глазами за несколько секунд, без необходимости поднимать переписку в мессенджере.
Где массовая правка опасна — и что перезатрёт ближайшая синхронизация
Раз уж мы говорим начистоту про инструмент, который даёт колоссальную власть над тысячами карточек одним нажатием кнопки, честно будет сказать и о том, где эта власть превращается в риск. Первое и самое важное правило, которое я вбиваю в голову каждому клиенту, — никогда не трогайте URL и слаг товара в массовых операциях, даже если технически поле доступно для редактирования. Адрес страницы товара — это фундамент накопленного SEO-веса: ссылки, которые вели на этот адрес годами, позиции в выдаче, поведенческие метрики, которые поисковик уже накопил именно для этого URL. Массовая замена подстроки в слаге ради, скажем, унификации формата у сотни товаров одним найти-заменить может показаться безобидной технической уборкой, а на деле это массовое обнуление истории URL, которое без грамотно настроенных редиректов оборачивается провалом органического трафика. Если вам действительно нужно поменять структуру адресов — это отдельная, гораздо более аккуратная операция через 301-редиректы, а не через инструмент массового редактирования полей.
Есть и менее очевидный риск, связанный именно с вариативными товарами. Когда категория или атрибут меняются массово на уровне родительского товара, вариации не всегда наследуют изменение автоматически — это зависит от того, какое именно поле меняется и как настроена связь атрибута с вариацией внутри WooCommerce. Цена и остаток у вариаций хранятся отдельно от родителя, и массовая операция над родительскими товарами их не трогает: если вам нужно поднять цену у всех фасовок конкретного товара, нужно либо явно включить вариации в выборку через отдельный параметр запроса, либо обрабатывать их отдельным проходом. Мелочь, о которой легко забыть в горячке большой ценовой ревизии, и именно поэтому я рекомендую всегда сверяться с предпросмотром — там сразу видно, сколько именно записей затронет операция, и если ожидались, скажем, четыре тысячи строк с учётом вариаций, а показывается тысяча, это повод остановиться и разобраться, прежде чем нажимать «применить».
Второй практический риск — это столкновение массовой правки с автоматической синхронизацией из внешнего источника. Если ваш каталог подпитывается из 1С, как я подробно разбирал в статье про синхронизацию WooCommerce с 1С:УТ, нужно чётко понимать, какие поля считаются «источником истины» на стороне учётной системы, а какие — вашей собственной зоной ответственности на сайте. Цена и остаток в подавляющем большинстве конфигураций синхронизации приходят из 1С и обновляются каждой синхронизацией — если вы вручную поднимете цену массовой операцией, а через час пройдёт плановый обмен, который затирает цену обратно значением из учётной системы, ваша ручная правка просто исчезнет без всякого предупреждения, и вы решите, что инструмент не сработал, хотя на самом деле сработал корректно, просто был перезаписан следующим циклом синхронизации. А вот описание, SEO-поля, дополнительные атрибуты для фильтрации на сайте — это обычно зона, которую 1С не трогает вообще, и там массовая правка держится ровно до тех пор, пока вы сами её не измените снова. Прежде чем разворачивать масштабную кампанию по правке каталога, разумно свериться с тем, кто в вашей связке сайт-1С является хозяином конкретного поля — это пять минут разговора с интегратором, которые экономят часы недоумения впоследствии.
Справедливости ради — экспорт и импорт CSV из модуля никуда не делись, и я считаю это правильным решением, а не отступлением от заявленной идеи. Если вам нужно выгрузить прайс-лист для дилера, отдать данные бухгалтерии для сверки или отправить каталог партнёру для размещения на маркетплейсе — эта возможность осталась ровно там же, в том же интерфейсе. Разница в философии не в том, что CSV исчез как класс, а в том, что он перестал быть единственным способом что-то поменять внутри своего же каталога. Экспорт — для передачи данных наружу. Редактирование — внутри системы, без единого файла-посредника между вами и базой данных.
Если сравнивать этот подход с тем, что предлагает рынок — отдельные плагины экспорта-импорта вроде WP All Import с надстройками для массового редактирования (лицензия на такую связку для одного сайта обычно от девяноста-ста долларов в год, плюс отдельная надстройка Bulk Edit сверху), или самописные скрипты на wp-cli, которые пишет штатный разработчик и которые потом некому поддерживать, когда этот разработчик уходит, — разница в первую очередь философская. Отдельный импортёр по своей природе работает через файл-посредник, а значит, наследует все проблемы, которые я описал в начале: кодировки, потерю форматирования, отсутствие предпросмотра до необратимого применения, отсутствие единой истории отмены. Это не значит, что такие инструменты плохие — для разовой миграции большого объёма данных из внешнего источника они на своём месте, и я подробно писал о похожей задаче в статье про перенос каталога с другого движка без потери позиций. Но для повседневной работы с уже существующим каталогом — обновить цены, почистить незаполненные карточки, перекинуть товары между категориями — таблица прямо внутри админки, без единого файла-посредника, оказывается на порядок быстрее и безопаснее.
Здесь же уместно вспомнить, что массовое редактирование редко существует само по себе — оно почти всегда идёт рука об руку с тем, как покупатель потом находит эти товары на витрине. Поправили категории и атрибуты пачкой — стоит проверить, что фасетные фильтры на витрине, о которых я рассказывал в статье про AJAX-фильтр каталога без перезагрузки страницы, подхватили изменения корректно, а счётчики по брендам и категориям пересчитались. Поправили описания и SEO-поля у большой выборки — стоит убедиться, что поисковый индекс, если он у вас на Typesense, переиндексировал изменённые карточки, а не продолжает показывать покупателям устаревший текст. Это не недостаток модуля, а естественное следствие того, что каталог — это не изолированный список цифр в базе, а живая система, где каждое звено связано с соседним.
Возвращаясь к истории с фрилансером и его счётом на сорок два часа — если бы у того магазина строительных материалов уже стоял модуль массового редактирования, та же самая задача, поднять цену на процент по категории плюс перекинуть несколько сотен товаров в другой раздел, заняла бы от силы двадцать минут вместе с проверкой предпросмотра. Не потому что фрилансер был плохим специалистом, а потому что задача, для решения которой нет нормального инструмента, неизбежно превращается в ручной труд, а ручной труд неизбежно стоит дорого и ошибается.
Из этой же истории я вынес ещё один урок, менее очевидный, чем экономия часов: инструмент массового редактирования меняет саму привычку работать с каталогом. Когда правка сотни товаров занимает пять минут, а не полдня, отношение к каталогу становится куда более живым — руководитель перестаёт откладывать «мелкие» улучшения на потом, потому что барьер входа в задачу практически исчез. Проставить SEO-заголовки у трёхсот товаров одной категории, почистить пустые описания, найденные умным фильтром, привести в порядок единицы измерения в весе после того, как поставщик сменил формат прайса, — все эти задачи раньше откладывались месяцами именно потому, что казались недостаточно важными, чтобы тратить на них рабочий день. Когда цена входа падает до пятнадцати минут, откладывать их становится попросту нелогично.
Я специально не привожу здесь эффектную цифру вроде «экономия 90% времени», потому что честная экономия зависит от размера каталога, частоты изменений и того, сколько сейчас стоит час работы человека, который эти изменения вносит вручную. Но если посчитать на пальцах — тот же магазин с сорока двумя часами ручного труда раз в полгода тратит на одну только эту операцию порядка ста двадцати тысяч рублей в год, не считая рисков ошибок, которые эти часы почти гарантированно с собой несут при таком объёме однообразной работы. Инструмент, который сводит эту же операцию к получасу работы штатного сотрудника, окупает себя не через абстрактный «рост эффективности», а через совершенно конкретную вычеркнутую статью расходов.
Стоит признать и обратную сторону: если у вас в каталоге полсотни товаров и меняются они раз в квартал, отдельный модуль массового редактирования — избыточная роскошь, и Excel действительно решит задачу быстрее, чем настройка любого нового инструмента. Порог, после которого табличный редактор внутри админки начинает окупать себя, — это где-то несколько сотен товаров при регулярных изменениях цен, категорий или контента. Ниже этого порога экономия не так заметна, выше — она становится решающей.
Если ваш каталог перевалил за пару тысяч товаров и вы до сих пор открываете Excel каждый раз, когда нужно что-то поправить массово, — вы платите за это либо временем, либо деньгами, либо тем и другим сразу, и чаще всего даже не замечаете, сколько именно, потому что эти часы размазаны по разным людям и разным месяцам, а не собраны в одну заметную строку бюджета, как счёт того фрилансера. В COS WP Woo массовый редактор входит в основной набор инструментов работы с каталогом наравне с умным поиском на Typesense и остальными модулями, из которых собран единый инструмент вместо зоопарка из десятка отдельных плагинов — фильтры, инлайн-правка, массовые операции с предпросмотром и полноценный откат по истории, без единой выгрузки за пределы сайта.
