Как связать два динамических списка и передать ссылку на текущий документ в 1С

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

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

Разберем типичные ошибки при реализации связки двух списков, выясним причины сбоев и рассмотрим правильные варианты решения задачи.

Почему форма зависает или аварийно закрывается

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

  1. Вызов контекстного сервера из обработчика смены строки: событие ПриАктивизацииСтроки (или ПриАктивизацииПоля) срабатывает при каждом перемещении курсора, в том числе при быстром листании стрелками на клавиатуре. Вызов тяжелых процедур с директивой &НаСервере перегружает канал клиент-серверного взаимодействия и приводит к сбою тонкого клиента.
  2. Использование клиентских свойств на сервере: свойство Элементы.Список.ТекущиеДанные доступно исключительно на клиенте. Попытка обратиться к нему внутри процедуры &НаСервере возвращает Неопределено либо вызывает исключение.
  3. Отсутствие проверки на заполненность: при первоначальном открытии формы, переключении закладок, установке фильтров или в пустом списке ТекущиеДанные равны Неопределено. Попытка прочитать вложенное свойство ТекущиеДанные.Ссылка без проверки приводит к ошибке выполнения.
  4. Накопление элементов отбора: постоянное добавление новых условий в Отбор.Элементы без очистки или перезаписи замусоривает настройки динамического списка и ухудшает производительность.

Способ 1. Установка параметров подчиненного динамического списка на клиенте

Самый производительный и правильный способ организации связки двух таблиц — создание второго динамического списка с параметрическим запросом. У динамического списка есть свойство Параметры, управление которым доступно непосредственно в контексте &НаКлиенте без единого серверного вызова.

Разберем по шагам настройку подчиненного списка:

  1. Создаем реквизит формы ДокПоступления с типом ДинамическийСписок.
  2. В свойствах списка включаем флаг Произвольный запрос и пишем текст с параметром &СсылкаНаЗаказ.
  3. В событии основного списка СписокПриАктивизацииСтроки устанавливаем значение параметра.

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


&НаКлиенте
Процедура СписокПриАктивизацииСтроки(Элемент)
    ТекущиеДанные = Элементы.Список.ТекущиеДанные;
    
    Если ТекущиеДанные = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    ДокПоступления.Параметры.УстановитьЗначениеПараметра("СсылкаНаЗаказ", ТекущиеДанные.Ссылка);
КонецПроцедуры

Способ 2. Установка отбора в подчиненном динамическом списке на клиенте

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

Рассмотрим пример установки отбора с предварительной очисткой элементов:


&НаКлиенте
Процедура СписокПриАктивизацииСтроки(Элемент)
    ТекущиеДанные = Элементы.Список.ТекущиеДанные;
    
    Если ТекущиеДанные = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    // Очищаем предыдущие элементы отбора
    ДокПоступления.Отбор.Элементы.Очистить();
    
    // Формируем новый элемент отбора
    ЭлементОтбора = ДокПоступления.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
    ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Заказ");
    ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
    ЭлементОтбора.ПравоеЗначение = ТекущиеДанные.Ссылка;
    ЭлементОтбора.Использование = Истина;
    ЭлементОтбора.РежимОтображения = РежимОтображенияЭлементаНастройкиКомпоновкиДанных.Недоступный;
КонецПроцедуры

Совет по оптимизации: вместо постоянной очистки и создания новых элементов можно один раз при открытии формы создать элемент отбора, а в событии ПриАктивизацииСтроки только менять его ПравоеЗначение и свойство Использование.

Способ 3. Отображение разнотипных документов оплаты в одном списке

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

Для этого в подчиненном динамическом списке включаем Произвольный запрос и объединяем выборки через оператор ОБЪЕДИНИТЬ ВСЕ:


ВЫБРАТЬ
    "Безналичная оплата" КАК ВидДокумента,
    Таблица.Ссылка КАК ДокументОплаты,
    Таблица.СуммаДокумента КАК Сумма,
    Таблица.Ссылка.Дата КАК Дата,
    Таблица.Заказ КАК Заказ
ИЗ
    Документ.ПоступлениеБезналичныхДенежныхСредств.РасшифровкаПлатежа КАК Таблица
ГДЕ
    Таблица.Заказ = &СсылкаНаЗаказ

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    "Кассовый ордер",
    Таблица.Ссылка,
    Таблица.СуммаДокумента,
    Таблица.Ссылка.Дата,
    Таблица.Заказ
ИЗ
    Документ.ПриходныйКассовыйОрдер.РасшифровкаПлатежа КАК Таблица
ГДЕ
    Таблица.Заказ = &СсылкаНаЗаказ

При таком подходе поле Основная таблица в динамическом списке оставляется пустым, а фильтрация выполняется установкой параметра &СсылкаНаЗаказ на клиенте.

Способ 4. Защита от частых вызовов через обработчик ожидания (Debounce)

Если для отображения подчиненных данных все же требуется выполнять сложные серверные вычисления, нельзя вызывать сервер напрямую из ПриАктивизацииСтроки. Чтобы избежать подвисания интерфейса при перемещении по строкам, используем подключение обработчика ожидания с задержкой 0.2–0.3 секунды.

Разберем реализацию схемы с подавлением дребезга:


&НаКлиенте
Процедура СписокПриАктивизацииСтроки(Элемент)
    // Сбрасываем предыдущий запланированный вызов
    ОтключитьОбработчикОжидания("ОбновитьПодчиненныеДанные");
    
    // Запускаем однократный таймер ожидания на 0.2 секунды
    ПодключитьОбработчикОжидания("ОбновитьПодчиненныеДанные", 0.2, Истина);
КонецПроцедуры

&НаКлиенте
Процедура ОбновитьПодчиненныеДанные()
    ТекущиеДанные = Элементы.Список.ТекущиеДанные;
    
    Если ТекущиеДанные = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    ОбновитьПодчиненныеДанныеНаСервере(ТекущиеДанные.Ссылка);
КонецПроцедуры

&НаСервере
Процедура ОбновитьПодчиненныеДанныеНаСервере(СсылкаНаЗаказ)
    // Выполнение серверного запроса и заполнение таблицы формы
КонецПроцедуры

Такой подход гарантирует, что серверный вызов произойдет только после того, как пользователь остановит курсор на конкретной строке списка.

← На главную