Как вывести реквизит из последней строки табличной части в форму списка документов?

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

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

Напрямую вывести колонку из табличной части в стандартный список документов невозможно, так как табличная часть — это подчиненная таблица со связью «один ко многим». В этой статье мы подробно рассмотрим, проанализируем и сравним три основных метода решения этой задачи в современных управляемых формах 1С 8.3, разберем их достоинства, недостатки и приведем конкретные примеры программного кода.

Решение 1. Физический реквизит в шапке документа (Классический подход)

Этот метод считается наиболее надежным и производительным с точки зрения скорости открытия и прокрутки динамического списка. Суть подхода заключается в том, что мы создаем реальный реквизит в шапке документа (например, ДатаПоследнегоЗвонка) и записываем туда значение из последней строки табличной части в момент сохранения документа.

Давайте разберем по шагам, как это реализовать:

  1. В конфигураторе откройте свойства нужного документа и добавьте новый реквизит шапки с типом Дата (или другим необходимым типом). Назовем его, например, РекНаш_ДатаЗвонкаПоследняя.
  2. Перейдите в модуль объекта документа. Заполнение реквизита должно происходить строго на стороне сервера в транзакции записи. Для этого идеально подходит обработчик события ПередЗаписью.

Рассмотрим пример кода, который выполняет эту задачу:


Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    
    // Проверяем, есть ли вообще строки в табличной части
    КоличествоСтрок = ТаблицаЗвонков.Количество();
    
    Если КоличествоСтрок > 0 Тогда
        // Получаем последнюю строку табличной части по ее индексу.
        // Индексация строк в 1С начинается с нуля, поэтому индекс последней строки равен КоличествоСтрок - 1
        ПоследняяСтрока = ТаблицаЗвонков[КоличествоСтрок - 1];
        
        // Копируем значение реквизита из последней строки в реквизит шапки
        РекНаш_ДатаЗвонкаПоследняя = ПоследняяСтрока.ДатаЗвонка;
    Иначе
        // Если табличная часть пуста, очищаем реквизит шапки (присваиваем пустую дату)
        РекНаш_ДатаЗвонкаПоследняя = '00010101000000';
    КонецЕсли;

КонецПроцедуры

Важное архитектурное замечание: Перенос данных нужно делать именно в модуле объекта в обработчике ПередЗаписью, а не в модуле формы. Это гарантирует актуальность данных при программном создании документов сторонними обработками, загрузке через интеграции или обмене данными.

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


Выборка = Документы.НашДокумент.Выбрать();
Пока Выборка.Следующий() Цикл
    
    ДокОбъект = Выборка.ПолучитьОбъект();
    КоличествоСтрок = ДокОбъект.ТаблицаЗвонков.Количество();
    
    Если КоличествоСтрок > 0 Тогда
        НоваяДата = ДокОбъект.ТаблицаЗвонков[КоличествоСтрок - 1].ДатаЗвонка;
        
        // Записываем данные только в случае, если значение действительно отличается
        Если ДокОбъект.РекНаш_ДатаЗвонкаПоследняя <> НоваяДата Тогда
            ДокОбъект.РекНаш_ДатаЗвонкаПоследняя = НоваяДата;
            
            // Отключаем лишние проверки и проведение по регистрам для ускорения процесса
            ДокОбъект.ОбменДанными.Загрузка = Истина;
            ДокОбъект.Записать();
        КонецЕсли;
    КонецЕсли;
    
КонецЦикла;

Решение 2. Произвольный запрос в Динамическом списке (Без изменения структуры данных)

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

Давайте проанализируем логику построения такого запроса. Нам нужно связать таблицу документов с табличной частью так, чтобы выбрать только ту строку табличной части, у которой номер строки является максимальным для каждого конкретного документа.

Настроим динамический список формы, установив флаг «Произвольный запрос», и напишем следующий текст запроса:


ВЫБРАТЬ
    Док.Ссылка КАК Ссылка,
    Док.Дата КАК Дата,
    Док.Номер КАК Номер,
    Док.Проведен КАК Проведен,
    ТЧ_Последняя.ДатаЗвонка КАК ДатаПоследнегоЗвонка
ИЗ
    Документ.НашДокумент КАК Док
    
    // Шаг 1. Находим максимальный номер строки для каждого документа
    ЛЕВОЕ СОЕДИНЕНИЕ (
        ВЫБРАТЬ
            ТЧ_Макс.Ссылка КАК Ссылка,
            МАКСИМУМ(ТЧ_Макс.НомерСтроки) КАК МаксНомерСтроки
        ИЗ
            Документ.НашДокумент.ТаблицаЗвонков КАК ТЧ_Макс
        ГРУППИРОВАТЬ ПО
            ТЧ_Макс.Ссылка
    ) КАК ВтМакс ПО Док.Ссылка = ВтМакс.Ссылка
    
    // Шаг 2. Соединяем с ТЧ по ссылке и по максимальному номеру строки, чтобы получить саму дату
    ЛЕВОЕ СОЕДИНЕНИЕ Документ.НашДокумент.ТаблицаЗвонков КАК ТЧ_Последняя
        ПО Док.Ссылка = ТЧ_Последняя.Ссылка 
        И ВтМакс.МаксНомерСтроки = ТЧ_Последняя.НомерСтроки

Важные правила при работе с динамическим списком:

Решение 3. Вынос истории в подчиненный Регистр сведений (Архитектурный стандарт)

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

Давайте выясним, как решить задачу наиболее профессионально:

  1. Создаем подчиненный регистр сведений (например, ИсторияЗвонковПоДокументам).
  2. Делаем измерение ДокументОснование (тип — ссылка на наш документ) и ресурс ДатаЗвонка.
  3. При добавлении звонка данные записываются напрямую в регистр сведений.
  4. В динамическом списке документов мы делаем простейшее левое соединение с виртуальной таблицей СрезПоследних созданного регистра сведений.

Этот способ работает невероятно быстро, поскольку СУБД моментально возвращает последние записи из индексированных таблиц срезов, а сам документ остается легким и быстродействующим.

Почему нельзя использовать старые события списка (ПриПолученииДанных и ПриВыводеСтроки)?

На старых форумах часто советуют использовать обработчики событий списков. В обычных формах это были события ПриВыводеСтроки и ПриПолученииДанных, в управляемых формах 8.3 аналогом выступает событие динамического списка ПриПолученииДанныхНаСервере.

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

Никогда не используйте этот метод для запросов к СУБД! Это приводит к катастрофическому падению производительности. При прокрутке списка система начнет выполнять десятки отдельных запросов к базе данных (так называемая проблема "запроса в цикле"). Экран начнет визуально дергаться, список будет "залипать", а сервер 1С и СУБД получат колоссальную паразитную нагрузку. Кроме того, по программно заполненным полям невозможно выполнять стандартные отборы и сортировки силами пользователя.

Резюме

Подводя итог, выделим оптимальные сценарии использования решений:

← На главную