При разработке функционала для массового изменения данных в списках (например, разархивирование номенклатуры), программисты часто сталкиваются с двумя классическими ошибками: «Итератор для значения не определен» и «Поле объекта недоступно для записи». Эти ошибки возникают из-за непонимания механизмов работы платформы 1С с выборками запросов и объектами базы данных. В этой статье мы подробно разберем причины возникновения этих проблем и построим корректный алгоритм массового обновления реквизитов.
Рассмотрим ситуацию, когда мы пытаемся обойти результат запроса. В коде автора была допущена следующая конструкция:
Для каждого Стр Из ВыборкаПоЗапросу Цикл
// Действия
КонецЦикла;
Проанализируем причину ошибки. Объект ВыборкаИзРезультатаЗапроса не является коллекцией в привычном понимании (как Массив или ТаблицаЗначений). Это динамический курсор, который позволяет перемещаться по записям только вперед. Платформа не реализует интерфейс итератора для этого объекта, поэтому цикл Для каждого с ним не работает.
Решение: Для обхода выборки необходимо использовать метод Следующий() внутри цикла Пока. Этот метод перемещает указатель на следующую запись и возвращает Истина, если запись существует.
Пока ВыборкаПоЗапросу.Следующий() Цикл
// Теперь мы можем обращаться к данным текущей строки выборки
КонецЦикла;
После исправления цикла часто возникает вторая проблема. Разработчик пытается изменить значение реквизита прямо в выборке:
ВыборкаПоЗапросу.РасшФормы_ВАрхиве = Ложь; // Ошибка здесь!
Выясним причину. Объект ВыборкаИзРезультатаЗапроса предназначен исключительно для чтения. Она содержит лишь копию данных, извлеченных из базы данных в момент выполнения запроса. Выборка не имеет связи с живыми объектами базы данных в контексте их модификации.
Решение: Чтобы изменить данные в базе, нам нужно получить Объект этого элемента справочника или документа по его ссылке. Только объект (СправочникОбъект) обладает методом Записать() и позволяет редактировать свои свойства.
Проанализируем пошагово, как должен выглядеть надежный код для разархивирования элементов:
Элементы.Список.ВыделенныеСтроки.Ссылка.ПолучитьОбъект(), меняем реквизит и вызываем Записать().Рассмотрим исправленный код процедуры:
&НаСервере
Процедура РасшФормы_РазархивироватьНаСервере(МассивСсылок)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Ссылка В(&МассивСсылок)";
Запрос.УстановитьПараметр("МассивСсылок", МассивСсылок);
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
// Получаем объект из ссылки
НоменклатураОбъект = Выборка.Ссылка.ПолучитьОбъект();
Если НоменклатураОбъект <> Неопределено Тогда
// Устанавливаем новое значение реквизита
НоменклатураОбъект.РасшФормы_ВАрхиве = Ложь;
// Записываем изменения в базу данных
Попытка
НоменклатураОбъект.Записать();
Исключение
ОбщегоНазначенияКлиентСервер.СообщитьПользователю(
НСтр("ru = 'Не удалось записать объект: '") + Выборка.Ссылка);
КонецПопытки;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
При реализации массовых операций важно учитывать вопросы производительности и целостности данных:
Использование транзакций: Если критично, чтобы либо все выбранные элементы сменили статус, либо ни один (в случае ошибки), оберните цикл в транзакцию. Это защитит данные от частичного обновления.
НачатьТранзакцию();
Попытка
// ... цикл записи объектов ...
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
ВызватьИсключение;
КонецПопытки;
Блокировка данных: При получении объекта для записи рекомендуется использовать механизм БлокировкаДанных. Это предотвратит коллизии, если другой пользователь попытается редактировать тот же элемент одновременно с вашей обработкой.
Права доступа: Если реквизит создан в расширении, убедитесь, что в расширение добавлены соответствующие Роли, дающие право на «Изменение» этого реквизита. Без прав на запись ошибка «Поле объекта недоступно для записи» может возникнуть даже при использовании ПолучитьОбъект().
Оптимизация для БСП: Если ваша конфигурация основана на Библиотеке стандартных подсистем (БСП), для записи объектов рекомендуется использовать функцию ОбщегоНазначения.ЗаписатьОбъект(). Она автоматически обрабатывает типичные ошибки блокировок и прав доступа.
Таким образом, мы выяснили, что для успешного решения задачи необходимо четко разделять понятия Ссылка, Выборка и Объект. Использование правильного цикла Пока Выборка.Следующий() и метода ПолучитьОбъект() — залог стабильной работы вашего кода.