Почему не отображается колонка с данными запроса динамического списка в расширении 1С?

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

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

Причина 1. Базовая ошибка позиционирования элемента на форме

Рассмотрим самую распространенную ситуацию, с которой и столкнулся автор обсуждения на форуме. При добавлении нового поля в запрос динамического списка через конструктор, платформа автоматически регистрирует это поле как новый доступный реквизит формы (внутри объекта ДинамическийСписок). Однако платформа не добавляет его на форму визуально в автоматическом режиме.

Разберем по шагам, как исправить эту ошибку:

  1. Откроем заимствованную форму списка в конфигураторе расширения.
  2. В окне «Реквизиты» (обычно в правом верхнем углу) развернем объект нашего динамического списка (например, реквизит Список) и найдем добавленное поле (например, СФ).
  3. Обратим внимание на дерево «Элементы» (в левом верхнем углу). Если нашего поля там нет, значит, мы забыли вывести его в пользовательский интерфейс.
  4. Перетащим мышкой реквизит СФ из дерева реквизитов в нужную позицию дерева элементов формы (например, внутрь таблицы списка документов).
  5. Сохраним расширение и проверим результат в режиме «1С:Предприятие».

Без этого простого действия поле будет существовать в выборке базы данных, но визуальный элемент управления для его отображения создан не будет.

Причина 2. Кэширование и пользовательские настройки формы

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

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

  1. В пользовательском режиме откроем проблемную форму списка.
  2. В правом верхнем углу формы нажмем кнопку «Еще».
  3. В выпадающем меню выберем пункт «Установить стандартные настройки».

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

Причина 3. Риски жесткого изменения запроса в расширении и правильная альтернатива

Когда мы заимствуем форму списка в расширение и правим текст запроса динамического списка «руками» через конструктор, мы берем на себя огромные риски при последующих обновлениях конфигурации. Платформа требует заимствовать в расширение абсолютно все объекты метаданных, участвующие в исходном запросе (справочники, документы, регистры). Если при обновлении типовой конфигурации вендор изменит исходный запрос списка, наше расширение заблокирует обновление или вызовет критическую ошибку в работе программы.

Проанализируем безопасное решение — программное добавление левого соединения к запросу динамического списка в обработчике ПриСозданииНаСервере с помощью объекта СхемаЗапроса. Это позволит динамически модифицировать запрос «на лету», не ломая структуру основного запроса и избегая заимствования лишних объектов в расширение.

Посмотрим на пример реализации такого кода:


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

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

Используя этот подход, мы гарантируем стабильность работы системы при любых обновлениях родительской конфигурации.

Причина 4. Оптимизация производительности через событие ПриПолученииДанныхНаСервере

Прямое левое соединение динамического списка с таблицей «Счет-фактура выданный» по полю «ДокументОснование» на базах данных объемом в сотни гигабайт может серьезно замедлить работу формы списка реализаций. Индексация по полю «ДокументОснование» в подчиненных документах не всегда спасает ситуацию при сложных отборах.

Рассмотрим альтернативный, максимально производительный подход с использованием события таблицы ПриПолученииДанныхНаСервере. Мы не будем усложнять основной запрос динамического списка тяжелыми соединениями. Вместо этого добавим на форму произвольную (не связанную с данными списка) колонку и будем заполнять ее только для тех строк, которые реально видны пользователю на экране в данный момент (обычно это 30–45 строк).

Разберем по шагам программный алгоритм оптимизации:

  1. Добавим в таблицу формы новую колонку с именем СФ и типом значения ДокументСсылка.СчетФактураВыданный. Данные для этой колонки указывать не нужно (оставим путь к данным пустым).
  2. Создадим обработчик события таблицы списка ПриПолученииДанныхНаСервере.
  3. Реализуем получение счетов-фактур точечным быстрым запросом к СУБД.

Посмотрим на пример кода:


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

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

Причина 5. Влияние функциональных опций и прав доступа

Иногда колонка пропадает с формы не из-за ошибок программирования, а по причине работы механизмов бизнес-логики платформы:

Причина 6. Использование свойства «Использовать всегда»

Бывают ситуации, когда новое поле добавлено в запрос динамического списка, но мы не планируем выводить его на форму визуально в виде колонки. Например, это поле необходимо нам исключительно в коде для выполнения расчетов, программного анализа или для настройки Условного оформления (чтобы раскрасить строки списка в зависимости от статуса счета-фактуры).

В целях оптимизации SQL-запросов платформа 1С умеет «вырезать» неиспользуемые поля из итогового запроса к СУБД. Если поле не выведено на форму как колонка, платформа решит, что оно не нужно, и исключит его. В результате при обращении к нему в коде мы получим ошибку или пустое значение.

Чтобы принудительно заставить платформу выбирать данные этого поля:

  1. Откроем свойства динамического списка в конфигураторе.
  2. Перейдем на вкладку «Настройка списка».
  3. Откроем вкладку «Использовать всегда».
  4. Найдем наше добавленное поле и установим напротив него флаг (галочку).

Теперь поле будет гарантированно присутствовать в выборке СУБД независимо от его видимости на форме.

← На главную