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