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