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