Массовое изменение данных в справочниках — одна из самых частых задач, с которыми сталкивается разработчик 1С. Будь то исправление ошибок ввода, очистка артикулов от лишних пробелов или нормализация наименований, принцип работы всегда строится на определенном алгоритме. Часто начинающие разработчики сталкиваются с ситуацией, когда запрос выполняется успешно, данные перебираются в цикле, но физического изменения в базе данных не происходит. Проанализируем, как правильно организовать этот процесс, и разберем нюансы, которые помогут сделать ваш код эффективным и безопасным.
Разберем базовую проблему, озвученную в теме: почему изменения не записываются сами по себе? Дело в том, что результат выполнения запроса или выборка справочника возвращают нам ссылки (тип СправочникСсылка). Ссылка — это неизменяемый идентификатор объекта в базе данных. Для того чтобы внести изменения, нам необходимо получить на основании этой ссылки объект (тип СправочникОбъект), который обладает методом Записать().
Рассмотрим по шагам правильную структуру кода для решения такой задачи:
Ссылка).ПолучитьОбъект().Записать().Посмотрим на пример кода, который решает поставленную задачу по очистке артикулов от пробелов:
Процедура ИсправитьАртикулы()
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка,
| Номенклатура.Артикул КАК Артикул
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Артикул ПОДОБНО ""% %"""; // Выбираем только те, где есть пробелы
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
// 1. Получаем объект для редактирования
ТекОбъект = Выборка.Ссылка.ПолучитьОбъект();
// 2. Выполняем преобразование строки
НовыйАртикул = СокрЛП(ТекОбъект.Артикул);
// 3. Проверяем, действительно ли значение изменилось, чтобы не перезаписывать лишний раз
Если ТекОбъект.Артикул <> НовыйАртикул Тогда
ТекОбъект.Артикул = НовыйАртикул;
// 4. Записываем изменения в базу данных
Попытка
ТекОбъект.Записать();
Исключение
Сообщить("Не удалось записать объект: " + Выборка.Ссылка + " Ошибка: " + КраткоеОписаниеОшибки(ИнформацияОбОшибке()));
КонецПопытки;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Проанализируем ситуацию, упомянутую автором в конце обсуждения: иногда пробелы — это не самое страшное. Часто пользователи вводят символы, которые выглядят одинаково, но имеют разные коды (например, латинская "X" и русская "Х"). В этом случае обычная функция СокрЛП не поможет, так как она работает только с краевыми пробелами.
Для решения проблемы перемешанных алфавитов или "неразрывных пробелов" (которые часто приходят из Excel и имеют код 160 вместо 32), рекомендуется использовать более глубокую очистку. Выясним причину, почему данные могут не находиться поиском даже после удаления пробелов: это именно разница в кодировке похожих букв.
Разберем пример функции для удаления всех видов пробелов, включая неразрывные:
Функция ПолнаяОчисткаСтроки(Знач ВходнаяСтрока)
// Удаляем стандартные пробелы по краям
Результат = СокрЛП(ВходнаяСтрока);
// Заменяем неразрывные пробелы (символ с кодом 160) на пустую строку
Результат = СтрЗаменить(Результат, Символы.НПП, "");
// Если нужно удалить вообще все пробелы внутри строки
Результат = СтрЗаменить(Результат, " ", "");
Возврат Результат;
КонецФункции
Если в вашем справочнике десятки тысяч элементов, простая циклическая запись может занять очень много времени. Рассмотрим подробнее профессиональные приемы ускорения этого процесса:
Использование транзакций. Когда мы вызываем Записать(), система 1С каждый раз открывает и закрывает транзакцию на уровне СУБД. Это "дорогая" операция. Если мы обернем цикл в одну большую транзакцию, скорость работы может увеличиться в несколько раз.
НачатьТранзакцию();
Пока Выборка.Следующий() Цикл
// ... логика получения объекта и изменения ...
ТекОбъект.Записать();
КонецЦикла;
ЗафиксироватьТранзакцию();
Отключение регистрации обменов. Если база участвует в обмене данными (РИБ или синхронизация), каждая запись будет создавать запись в таблице регистрации изменений. При массовой технической правке это может привести к тому, что огромный объем данных "полетит" во все узлы. Чтобы этого избежать, используем свойство ОбменДанными.Загрузка.
ТекОбъект = Выборка.Ссылка.ПолучитьОбъект();
ТекОбъект.ОбменДанными.Загрузка = Истина; // Отключает проверки и регистрацию изменений
ТекОбъект.Артикул = СокрЛП(ТекОбъект.Артикул);
ТекОбъект.Записать();
При работе в многопользовательской среде важно помнить, что объект, который вы пытаетесь изменить, может быть занят другим пользователем (открыта форма редактирования). В таком случае метод ПолучитьОбъект() или Записать() вызовет исключение. Всегда используйте конструкцию Попытка...Исключение, как показано в первом примере кода, чтобы ваша обработка не прервалась на середине пути из-за одной заблокированной карточки номенклатуры.
Прежде чем писать собственный код, проанализируем, нельзя ли решить задачу стандартными средствами. В большинстве типовых конфигураций 1С (УТ 11, БП 3.0, ERP) существует встроенная обработка "Групповое изменение реквизитов". Она позволяет не только заменять значения, но и выполнять произвольный код на языке 1С для каждого выбранного объекта. Это безопаснее и зачастую быстрее, чем написание внешней обработки "с нуля".
Подводя итог, можно сказать, что программное изменение справочника требует внимательного отношения к типам данных (Ссылка vs Объект), обработки исключительных ситуаций и понимания того, как работают строковые функции. Всегда проверяйте результат работы вашего кода на копии базы, прежде чем запускать массовое обновление артикулов в "боевой" системе.