Как отобразить движения регистра в дереве значений при выборе строки в динамическом списке?

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

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

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

Шаг 1. Организация взаимодействия на форме

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

На клиенте код обработчика будет выглядеть следующим образом:


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

Здесь мы получаем данные текущей выделенной строки через свойство Элементы.ИмяСписка.ТекущиеДанные. Это стандартный способ получить доступ ко всем полям строки динамического списка на клиенте. Затем вызываем две процедуры: одну серверную для основной работы с данными (ЗаполнениеДерева) и одну клиентскую для улучшения интерфейса (РазвернутьВеткуДерева).

Шаг 2. Получение данных о движениях на сервере

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


&НаСервере
Функция ПолучитьДолг(ФизЛицо)
    Запрос = Новый Запрос;
    Запрос.Текст =
        "ВЫБРАТЬ
        |    ВзаиморасчетыПрочиеДолги.Период КАК Период,
        |    ВзаиморасчетыПрочиеДолги.Регистратор КАК Регистратор,
        |    ВзаиморасчетыПрочиеДолги.Активность КАК Активность,
        |    ВЫБОР
        |        КОГДА ВзаиморасчетыПрочиеДолги.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Приход)
        |            ТОГДА ВзаиморасчетыПрочиеДолги.Сумма
        |        ИНАЧЕ 0 - ВзаиморасчетыПрочиеДолги.Сумма
        |    КОНЕЦ КАК Сумма,
        |    ВзаиморасчетыПрочиеДолги.ФизЛицо КАК ФизЛицо,
        |    ВзаиморасчетыПрочиеДолги.ДебиторКредитор КАК ДебиторКредитор,
        |    ВзаиморасчетыПрочиеДолги.Пользователь КАК Пользователь
        |ИЗ
        |    РегистрНакопления.ВзаиморасчетыПрочиеДолги КАК ВзаиморасчетыПрочиеДолги
        |ГДЕ
        |    ВзаиморасчетыПрочиеДолги.ФизЛицо = &ФизЛицо";

    Запрос.УстановитьПараметр("ФизЛицо", ФизЛицо);
    
    Результат = Запрос.Выполнить();
    ТаблицаРезультат = Результат.Выгрузить();
    
    Возврат ТаблицаРезультат;
КонецФункции

Важный момент: Обратите внимание на конструкцию ВЫБОР ... КОНЕЦ. Она позволяет нам сразу получить сумму с нужным знаком: положительную для прихода и отрицательную для расхода. Это удобно для отображения в дереве.

Еще одна частая ошибка, с которой можно столкнуться, — это несоответствие типов параметров в запросе. Если в регистре измерение ФизЛицо имеет тип СправочникСсылка.ФизЛица, а вы передаете в параметр строку, запрос не найдет данных. Убедитесь, что тип переменной, которую вы передаете в Запрос.УстановитьПараметр(), соответствует типу поля в базе данных.

Шаг 3. Заполнение дерева значений на сервере

Теперь, когда у нас есть данные, напишем основную процедуру, которая будет формировать дерево. Работа с реквизитами формы типа ДеревоЗначений на сервере имеет свою специфику: сначала нужно преобразовать реквизит в объект (РеквизитФормыВЗначение), выполнить все манипуляции, а затем преобразовать обратно (ЗначениеВРеквизитФормы).

Рассмотрим оптимизированный код процедуры:


&НаСервере
Процедура ЗаполнениеДерева(ФизЛицо, ТекущаяВыбраннаяСтрока)
    // 1. Преобразуем реквизит формы в объект "ДеревоЗначений"
    тДерево = РеквизитФормыВЗначение("ДеревоИсторияДвиженияДолга");
    
    // 2. Обязательно очищаем дерево от старых данных
    тДерево.Строки.Очистить();
    
    // 3. Добавляем корневую строку (само физлицо)
    КорневаяСтрокаДерева = тДерево.Строки.Добавить();
    КорневаяСтрокаДерева.ФизЛицо = ТекущаяВыбраннаяСтрока.ФизЛицо;
    КорневаяСтрокаДерева.Сумма = ТекущаяВыбраннаяСтрока.СуммаОстаток;
    
    // 4. Получаем данные о движениях
    ТаблицаДолги = ПолучитьДолг(ФизЛицо);
    
    // 5. В цикле заполняем дочерние строки движениями
    Для каждого Строка Из ТаблицаДолги Цикл
        СтрокаДерева = КорневаяСтрокаДерева.Строки.Добавить();
        СтрокаДерева.Сумма = Строка.Сумма;
        СтрокаДерева.ФизЛицо = Строка.ДебиторКредитор; // В данном примере, видимо, используется поле ДебиторКредитор
        СтрокаДерева.Документ = Строка.Регистратор;
        СтрокаДерева.Пользователь = Строка.Пользователь;
    КонецЦикла;
    
    // 6. Записываем измененное дерево обратно в реквизит формы
    ЗначениеВРеквизитФормы(тДерево, "ДеревоИсторияДвиженияДолга");
    
    // 7. (Опционально) Программно переключаемся на страницу с деревом, если она не активна
    ПереходНаСтраницу = ЭтаФорма.Элементы.СтраницыДолги.ПодчиненныеЭлементы.ДолгиИсторияДвижений;
    ЭтаФорма.Элементы.СтраницыДолги.ТекущаяСтраница = ПереходНаСтраницу;
КонецПроцедуры

Проанализируем ключевые моменты этого кода:

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

Шаг 4. Разворачивание узлов дерева на клиенте

После того как сервер заполнил дерево данными и вернул управление на клиент, оно будет отображено в свернутом виде. Чтобы пользователю не приходилось вручную кликать на "+", развернем корневой узел программно.


&НаКлиенте
Процедура РазвернутьВеткуДерева()
    // Получаем коллекцию элементов дерева верхнего уровня
    КоллекцияЭлементовДерева = ДеревоИсторияДвиженияДолга.ПолучитьЭлементы();
    
    // Обходим коллекцию и разворачиваем каждый элемент
    Для Каждого Строка Из КоллекцияЭлементовДерева Цикл
        ИдентификаторСтроки = Строка.ПолучитьИдентификатор();
        Элементы.ДеревоИсторияДвиженияДолга.Развернуть(ИдентификаторСтроки);
    КонецЦикла;
КонецПроцедуры

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

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

← На главную