Как реализовать массовое редактирование выделенных строк в динамическом списке 1С?

Программист 1С v8.3 (Управляемые формы) Управленческий учет
← На главную

В разработке на управляемых формах часто возникает задача предоставить пользователю возможность редактировать данные не по одной строке, а массово. Типичный пример: установка флага участия или ответственного лица для целого списка сотрудников. Если динамический список построен на основе произвольного запроса (например, объединяющего данные справочников и регистров), стандартные механизмы редактирования могут быть ограничены. Разберем подробно, как программно обработать выделенные строки, получить их значения и корректно внести изменения в базу данных.

Проблема: как получить данные из «номера строки»?

При работе с динамическим списком программисты часто сталкиваются с тем, что свойство ВыделенныеСтроки возвращает массив идентификаторов, а не сами данные. Если для текущей строки мы можем легко обратиться к свойству ТекущиеДанные, то для обработки множественного выбора нам нужен другой подход. Рассмотрим ситуацию, когда у нас есть список, отображающий «матрицу ответственности» (физические лица и услуги), и нам нужно инвертировать флаг участия для всех выбранных строк.

Способ 1. Использование метода ДанныеСтроки (Клиентский подход)

Самый простой способ получить значения полей для выделенных строк — использование метода ДанныеСтроки() элемента формы (таблицы). Этот метод принимает идентификатор строки и возвращает структуру со значениями полей этой строки.

Рассмотрим пример реализации в обработчике события выбора или по нажатию специальной кнопки:


&НаКлиенте
Процедура ИзменитьФлагДляВыделенных(Команда)
    
    // Получаем массив идентификаторов выделенных строк
    ВыделенныеСтроки = Элементы.Список.ВыделенныеСтроки;
    
    Для Каждого Идентификатор Из ВыделенныеСтроки Цикл
        
        // Получаем данные конкретной строки по её ID
        ДанныеСтроки = Элементы.Список.ДанныеСтроки(Идентификатор);
        
        // Проверяем, что данные были получены (могут быть не определены, если строка не в кэше)
        Если ДанныеСтроки <> Неопределено Тогда
            
            // Вызываем серверную процедуру для записи изменений в БД
            ЗаписатьИзмененияВРегистрНаСервере(
                ДанныеСтроки.Организация, 
                ДанныеСтроки.ФизическоеЛицо, 
                ДанныеСтроки.Услуга, 
                ДанныеСтроки.ФлагУчастия
            );
            
        КонецЕсли;
        
    КонецЦикла;
    
    // Обновляем список, чтобы пользователь увидел изменения
    Элементы.Список.Обновить();
    
КонецПроцедуры

Важное ограничение: Метод ДанныеСтроки() работает только с теми данными, которые в данный момент загружены в клиентский кэш. Динамический список подгружает данные порциями. Если пользователь выделит 2000 строк и попытается их обработать, для тех строк, которые еще не были отрисованы на экране, метод вернет Неопределено.

Способ 2. Оптимизированная серверная обработка (Профессиональный подход)

Чтобы избежать проблем с кэшем и минимизировать количество клиент-серверных вызовов (которые сильно замедляют работу интерфейса), правильно будет собрать все идентификаторы в один массив и передать их на сервер одним вызовом.

Проанализируем ситуацию: если наш динамический список имеет «Основную таблицу», идентификаторами строк будут ссылки или ключи записей. Если же это произвольный запрос (как в примере автора с UNION ALL), идентификатором может выступать ключ, автоматически сформированный платформой.

Рекомендуемый алгоритм работы:

  1. Собрать массив идентификаторов ВыделенныеСтроки на клиенте.
  2. Передать этот массив на сервер.
  3. На сервере, если список имеет основную таблицу, получить данные запросом или через менеджер.
  4. Если основной таблицы нет, придется использовать данные, переданные с клиента, либо выполнять дополнительный запрос к базе данных, опираясь на уникальные поля.

Посмотрим на пример серверного метода, который обрабатывает сразу весь массив:


&НаСервере
Процедура МассовоеИзменениеНаСервере(МассивИдентификаторов)
    
    Для Каждого СтрокаID Из МассивИдентификаторов Цикл
        
        // В случае произвольного запроса идентификатор часто совпадает с ТекущиеДанные
        // Если ключ строки составной, логика получения данных усложняется
        // Здесь мы предполагаем, что на сервер переданы уже готовые структуры данных
        
        МенеджерЗаписи = РегистрыСведений.ИТ_УслугиПоФизЛицам.СоздатьМенеджерЗаписи();
        МенеджерЗаписи.Организация = СтрокаID.Организация;
        МенеджерЗаписи.ФизическоеЛицо = СтрокаID.ФизическоеЛицо;
        МенеджерЗаписи.Услуга = СтрокаID.Услуга;
        
        // Инвертируем флаг
        МенеджерЗаписи.Прочитать();
        МенеджерЗаписи.ФлагУчастия = НЕ МенеджерЗаписи.ФлагУчастия;
        МенеджерЗаписи.Записать();
        
    КонецЦикла;
    
КонецПроцедуры

Разбор запроса динамического списка

Выясним причину, почему автору темы было сложно работать с идентификаторами. В его запросе используется ГРУППИРОВАТЬ ПО и ОБЪЕДИНИТЬ ВСЕ. В таких случаях динамический список может не иметь четкого соответствия одной физической таблице БД.

Чтобы работа с выделенными строками была стабильной, проанализируем запрос:


ВЫБРАТЬ
    ВложЗапрос.ФизическоеЛицо,
    ВложЗапрос.Услуга,
    МАКСИМУМ(ВложЗапрос.ФлагУчастия) КАК ФлагУчастия
ИЗ (...) КАК ВложЗапрос
СГРУППИРОВАТЬ ПО
    ВложЗапрос.ФизическоеЛицо,
    ВложЗапрос.Услуга

В настройках такого динамического списка в конфигураторе крайне рекомендуется вручную указать Ключ строки. Для данного примера ключом будет комбинация ФизическоеЛицо и Услуга. Это позволит платформе однозначно идентифицировать каждую строку, даже если данные меняются.

Обновление данных и механизм оповещений

Рассмотрим распространенную ошибку: изменение данных напрямую в ТекущиеДанные на клиенте. Как справедливо замечено в обсуждении, это «багофича». Изменения, внесенные только в визуальную строку, будут потеряны при первой же прокрутке списка или обновлении страницы, так как динамический список перечитает данные из базы.

Правильная последовательность действий:

  1. Записываем данные в базу (регистр, справочник) на сервере.
  2. Используем метод Обновить() для элемента формы.
  3. Для того чтобы другие открытые формы (например, отчеты или списки в других окнах) узнали об изменениях, вызываем ОповеститьОбИзменении().

Пример корректного завершения операции:


&НаКлиенте
Процедура ПослеМассовогоИзменения()
    // Сообщаем системе, что данные регистра изменились
    ОповеститьОбИзменении(Тип("РегистрСведенийЗапись.ИТ_УслугиПоФизЛицам"));
    // Принудительно обновляем текущий список
    Элементы.ТаблицаВвода.Обновить();
КонецПроцедуры

Важные нюансы и оптимизация

Проанализируем ситуацию с большими объемами данных. Если в списке 2500 сотрудников и 40 услуг, общее количество комбинаций достигает 100 000. При массовом выделении (Ctrl+A) и попытке записи каждой строки через МенеджерЗаписи сервер может столкнуться с блокировками или таймаутом.

Рекомендации для высокой производительности:

Таким образом, мы разобрали, что для редактирования выделенных строк в динамическом списке необходимо комбинировать получение идентификаторов на клиенте через ВыделенныеСтроки, чтение данных через ДанныеСтроки (с учетом ограничений кэша) и последующую транзакционную запись на сервере с обязательным обновлением интерфейса через механизм оповещений.

← На главную