В процессе разработки на платформе 1С:Предприятие часто возникает задача массового изменения данных. Типичный сценарий: у нас есть документ или внешняя обработка с табличной частью, в которой указаны ссылки на элементы справочника (например, СерииНоменклатуры). Нам нужно изменить какой-либо реквизит внутри этих элементов (например, дату ГоденДо). Рассмотрим, почему простые попытки присвоить значение «через точку» не работают и как правильно реализовать эту логику.
Проанализируем типичную ситуацию, с которой сталкиваются новички. При попытке выполнить код вида СтрокаТЧ.Серия.ГоденДо = НоваяДата; система выдает ошибку: «Поле объекта недоступно для записи». Выясним причину: в табличной части хранится Ссылка (тип СправочникСсылка.СерииНоменклатуры). Ссылка — это всего лишь уникальный идентификатор записи в базе данных, она предназначена для чтения и поиска, но не для редактирования.
Для изменения данных нам необходимо получить Объект. Рассмотрим правильный алгоритм действий:
ПолучитьОбъект() для этой ссылки.Записать() для фиксации изменений в базе данных.Рассмотрим подробнее программный код, который позволяет обойти строки табличной части и обновить данные в связанном справочнике. Предположим, у нас есть обработка, где в шапке указан новый СрокГодности, а в табличной части Инфо перечислены серии.
// Перебираем строки нашей табличной части (на форме обработки)
Для Каждого Стр Из Объект.Инфо Цикл
// Проверяем, заполнена ли ссылка на серию
Если ЗначениеЗаполнено(Стр.Серия) Тогда
// Получаем объект справочника для редактирования
СерияОбъект = Стр.Серия.ПолучитьОбъект();
// Устанавливаем новое значение реквизита в объекте справочника
СерияОбъект.ГоденДо = Объект.СрокГодности;
// Записываем изменения в базу данных
Попытка
СерияОбъект.Записать();
Исключение
Сообщить("Не удалось записать серию: " + Стр.Серия + ". Ошибка: " + ОписаниеОшибки());
КонецПопытки;
// Если нужно обновить дату и в самой табличной части для визуализации
Стр.ГоденДо = Объект.СрокГодности;
КонецЕсли;
КонецЦикла;
Важно обратить внимание: если вы меняете данные в справочнике, это не всегда автоматически обновляет отображение данных в форме документа, если эти данные продублированы в табличной части самого документа. Разберем этот момент далее.
Иногда реквизит (например, ГоденДо) хранится и в справочнике Серии, и в табличной части документа ПриходныйОрдер. В этом случае недостаточно просто изменить справочник — нужно обновить и сам документ. Проанализируем ситуацию, когда нам нужно обновить оба объекта одновременно.
// 1. Получаем объект документа, который выбрали в обработке
ДокОбъект = Объект.СсылкаНаДокумент.ПолучитьОбъект();
// 2. Перебираем строки документа
Для Каждого СтрокаДок Из ДокОбъект.Товары Цикл
// Обновляем справочник Серии
Если ЗначениеЗаполнено(СтрокаДок.Серия) Тогда
ОбъектСерии = СтрокаДок.Серия.ПолучитьОбъект();
ОбъектСерии.ГоденДо = Объект.НовыйСрокГодности;
ОбъектСерии.Записать();
КонецЕсли;
// Обновляем реквизит в самой строке документа
СтрокаДок.ГоденДо = Объект.НовыйСрокГодности;
КонецЦикла;
// 3. Записываем сам документ
ДокОбъект.Записать(РежимЗаписиДокумента.Запись);
Рассмотрим ситуацию, когда в документе на 1000 строк используется всего 5 уникальных серий. Если мы будем вызывать ПолучитьОбъект() и Записать() в каждой строке, мы выполним 1000 обращений к базе данных вместо 5. Это критически замедляет работу системы. Проанализируем, как оптимизировать этот процесс с помощью соответствия (коллекция Соответствие).
// Создаем коллекцию для хранения уже обработанных серий
ОбработанныеСерии = Новый Соответствие;
Для Каждого Стр Из Объект.Инфо Цикл
СсылкаНаСерию = Стр.Серия;
// Если мы еще не записывали эту серию в данном цикле
Если ОбработанныеСерии[СсылкаНаСерию] = Неопределено Тогда
ОбъектСерии = СсылкаНаСерию.ПолучитьОбъект();
ОбъектСерии.ГоденДо = Объект.СрокГодности;
ОбъектСерии.Записать();
// Помечаем серию как обработанную
ОбработанныеСерии.Вставить(СсылкаНаСерию, Истина);
КонецЕсли;
КонецЦикла;
Такой подход сокращает количество транзакций и значительно повышает производительность обработки данных.
При программном изменении данных всегда следует учитывать контекст выполнения и бизнес-логику системы. Разберем ключевые рекомендации:
НачатьТранзакцию() и ЗафиксироватьТранзакцию(). Если при записи одной из 100 серий произойдет ошибка, транзакция откатит все изменения, и вы не получите ситуацию, когда часть данных обновлена, а часть — нет.ПолучитьОбъект() доступен только на стороне сервера. Всю логику по обходу табличных частей и записи объектов необходимо выносить в процедуры с директивой &НаСервере.Подведем итог: для записи данных «на форме» (а на самом деле в базу через форму) мы всегда должны переходить от Ссылки к Объекту, выполнять модификацию и не забывать про метод Записать(). Тщательный подход к оптимизации количества записей и учет движений по регистрам позволит вам создавать надежные и быстрые инструменты для корректировки данных в 1С.