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