Как вывести картинку состояния документа в табличную часть на обычных формах 1С?

Программист 1С v8.3 (Обычные формы) IT и автоматизация бизнеса
← На главную

При разработке интерфейсов на обычных формах в «1С:Предприятии» часто возникает задача визуализировать состояние документов в табличной части. Пользователям гораздо удобнее ориентироваться в списке, когда они видят привычные пиктограммы: зеленую галочку для проведенного документа, обычный листок для записанного и красный крестик для помеченного на удаление.

В этой статье мы подробно разберем, как реализовать этот механизм. Выясним, почему стандартные списки работают «сами по себе», проанализируем ошибки в коде начинающих разработчиков и напишем максимально быстрое, производительное и эстетически красивое решение с использованием механизма ПриПолученииДанных.

Почему типовые журналы и списки документов работают без кода?

Разберем сначала вопрос, который часто ставит в тупик начинающих разработчиков: почему в типовых формах списков и журналов значки состояния выводятся автоматически, без единой строчки кода в модуле формы? Здесь работает один из двух встроенных механизмов платформы:

  1. Автоматический поставщик данных (для динамических списков): Если табличное поле связано с источником данных типа ДокументСписок, платформа «1С:Предприятие» берет управление отображением на себя. Она сама считывает системные поля Проведен и ПометкаУдаления, сопоставляет их со стандартной коллекцией картинок и выводит пиктограмму в самую левую колонку.
  2. Настройка через свойства колонки (для произвольных таблиц): Настроить вывод картинок без программирования можно непосредственно в конфигураторе через палитру свойств. Для этого у табличного поля в свойстве «Картинки строк» указывается коллекция (например, БиблиотекаКартинок.СостоянияДокумента), а у конкретной колонки в свойстве «Данные картинки» прописывается путь к числовому реквизиту строки (индексу картинки). Платформа автоматически свяжет число в строке с соответствующим кадром из коллекции.

Однако, если наша задача — вывести динамическое состояние реального документа, ссылка на который хранится в обычной табличной части (коллекции строк), стандартные автоматические механизмы настройки свойств не помогут. Нам придется написать код. Но сделать это нужно правильно.

Критика простого решения: почему нельзя использовать дополнительный реквизит и событие ПриВыводеСтроки?

Рассмотрим типичный подход, который часто выбирают разработчики на первом этапе (именно его предложил автор темы на форуме):

Выясним, почему этот подход содержит критические архитектурные недостатки:

Проблема актуальности данных: Если статус документа хранится непосредственно в табличной части, то при изменении состояния документа в базе данных (например, другой пользователь провел или распровел документ), статус в вашей таблице станет неактуальным. Нам нужно получать состояние документа динамически «на лету» в момент отображения строки на экране.

Проблема производительности (N+1 запрос): Если вы решите отказаться от хранения статуса в ТЧ и будете получать его динамически в событии ПриВыводеСтроки через обращение к свойствам ссылки (например, ДанныеСтроки.Документ.Проведен), система начнет отправлять отдельный SQL-запрос к базе данных для каждой отрисованной строки. При прокрутке списка из 100 строк платформа выполнит 100 обращений к СУБД, что вызовет сильные зависания интерфейса. Событие ПриВыводеСтроки предназначено только для быстрого оформления ячейки на основе уже имеющихся в строке данных!

Правильный подход: оптимизация через событие ПриПолученииДанных

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

Рассмотрим по шагам, как реализовать наиболее красивое и производительное решение.

Шаг 1. Настройка колонки в конфигураторе

Выводить иконку состояния в отдельной колонке — не самое красивое решение. Гораздо эстетичнее выводить ее прямо в ячейке с самим документом (в колонке со ссылкой), как это сделано в стандартных журналах 1С. Для этого нам понадобится стандартная системная коллекция картинок БиблиотекаКартинок.СостоянияДокумента. В этой ленте:

Сначала свяжем нашу колонку с этой коллекцией картинок. Это можно сделать программно при открытии формы (в обработчике ПриОткрытии):


Процедура ПриОткрытии()
    // Укажем колонке "Документ" источник картинок из системной библиотеки
    ЭлементыФормы.ТабличнаяЧасть.Колонки.Документ.КартинкиСтрок = БиблиотекаКартинок.СостоянияДокумента;
КонецПроцедуры

Шаг 2. Реализация обработчика ПриПолученииДанных

Теперь напишем обработчик события ПриПолученииДанных для нашего табличного поля. Мы соберем ссылки всех документов, которые видны на экране, одним запросом получим их состояния (проведен/помечен на удаление) и распределим картинки по ячейкам.

Проанализируем пример кода:


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

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

Если в вашей табличной части колонка «Документ» имеет составной тип данных (могут храниться разные виды документов), то прямой запрос к одной таблице не подойдет. В этом случае мы можем применить универсальный способ получения статусов без ручного написания сложных запросов с объединениями. Мы воспользуемся тем, что свойства Проведен и ПометкаУдаления есть у всех документов в системе. Быстро получить их значения для массива разнородных ссылок можно с помощью следующей конструкции в запросе:


// Универсальный запрос для составного типа данных
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |    Выразить(ТаблицаДокументов.Ссылка Как Документ.ПродажаТоваров).Проведен КАК Проведен,
    |    // ... или более простой способ получения состояния через метаданные в цикле (для небольших списков),
    |    // но самым производительным решением будет динамическая сборка текста запроса 
    |    // по метаданным типов, представленных в массиве документов.";

Для динамического построения универсального запроса по метаданным мы можем сгруппировать массив документов по их типам, а затем для каждого уникального типа программно сформировать пакетный запрос. Посмотрим, как это выглядит на практике:


// Сгруппируем документы по типам
СоответствиеТипов = Новый Соответствие;
Для Каждого Ссылка Из МассивДокументов Цикл
    ТипДокумента = Ссылка.Метаданные();
    Если СоответствиеТипов[ТипДокумента] = Неопределено Тогда
        СоответствиеТипов.Вставить(ТипДокумента, Новый Массив);
    КонецЕсли;
    СоответствиеТипов[ТипДокумента].Добавить(Ссылка);
КонецЦикла;

// Строим текст запроса динамически
ТекстЗапроса = "";
НомерОбъединения = 0;

Для Каждого КлючИЗначение Из СоответствиеТипов Цикл
    ИмяТаблицы = КлючИЗначение.Ключ.ПолноеИмя();
    ИмяПараметра = "Массив_" + НомерОбъединения;
    
    Если НомерОбъединения > 0 Тогда
        ТекстЗапроса = ТекстЗапроса + " ОБЪЕДИНИТЬ ВСЕ ";
    КонецЕсли;
    
    ТекстЗапроса = ТекстЗапроса + 
        "ВЫБРАТЬ Ссылка, Проведен, ПометкаУдаления ИЗ " + ИмяТаблицы + " ГДЕ Ссылка В (&" + ИмяПараметра + ")";
        
    Запрос.УстановитьПараметр(ИмяПараметра, КлючИЗначение.Значение);
    НомерОбъединения = НомерОбъединения + 1;
КонецЦикла;

Запрос.Текст = ТекстЗапроса;

Этот подход гарантирует максимальную скорость работы интерфейса 1С. Мы получаем ровно один SQL-запрос к базе данных, содержащий только видимые на экране документы, независимо от того, сколько различных типов документов добавлено в табличную часть.

Подведем итоги

Мы подробно разобрали задачу вывода картинок состояния документов в обычных формах 1С. Подводя итог нашей совместной работе, выделим главные правила:

  1. Не храните динамический статус в ТЧ: Состояние проведения и удаления документов должно читаться из информационной базы в реальном времени.
  2. Используйте только ПриПолученииДанных: Забудьте про обращение к реквизитам через точку в событии ПриВыводеСтроки, если не хотите, чтобы интерфейс программы зависал у пользователей.
  3. Группируйте запросы: Читайте данные пакетом под всю видимую область экрана с помощью пакетных или динамически собранных запросов.
  4. Соблюдайте стандарты интерфейса: Выводите картинки состояния прямо в колонке документа, используя стандартную системную библиотеку картинок СостоянияДокумента.
← На главную