Как вывести и отсортировать таблицу документов на основе двусвязного списка в 1С

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

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

Анализ проблемы структуры двусвязного списка

Использование двусвязного списка в чистом виде (через ссылки на соседние элементы) идеально подходит для низкоуровневых алгоритмов, но создает серьезные трудности для СУБД и механизмов 1С, таких как ДинамическийСписок. Проблема заключается в том, что SQL-запрос не может «на лету» выстроить цепочку связей без рекурсии или итеративного обхода. Если мы попытаемся вывести данные просто «как есть», мы получим хаотичный набор записей. Разберем основные стратегии решения этой задачи.

Решение 1: Использование вспомогательного числового поля «Порядок»

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

Проанализируем алгоритм перемещения элемента. Чтобы избежать пересчета всех записей справочника при каждом сдвиге (что критично при больших объемах данных), применим метод дробных индексов или разряженных чисел. Вместо весов 1, 2, 3 будем использовать, например, 100, 200, 300. Если пользователь перемещает документ между позициями 100 и 200, мы просто присвоим ему вес 150.

Рассмотрим пример кода для обновления порядка при перемещении элемента:


Процедура ОбновитьПорядокЗаявки(ТекущаяСвязь, СвязьВыше, СвязьНиже)
    
    НовыйПорядок = 0;
    
    Если СвязьВыше = Неопределено Тогда
        // Перемещение в самое начало
        НовыйПорядок = СвязьНиже.Порядок / 2;
    ИначеЕсли СвязьНиже = Неопределено Тогда
        // Перемещение в самый конец
        НовыйПорядок = СвязьВыше.Порядок + 100;
    Иначе
        // Вставка между двумя элементами
        НовыйПорядок = СвязьВыше.Порядок + (СвязьНиже.Порядок - СвязьВыше.Порядок) / 2;
    КонецЕсли;
    
    ОбъектСвязи = ТекущаяСвязь.ПолучитьОбъект();
    ОбъектСвязи.Порядок = НовыйПорядок;
    ОбъектСвязи.Записать();
    
КонецПроцедуры

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

Решение 2: Рекурсивные запросы (CTE) для платформы 8.3.24+

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

Рассмотрим структуру такого запроса:


ВЫБРАТЬ
    Связи.Ссылка,
    Связи.Заявка,
    0 КАК Уровень
ИЗ
    Справочник.Связи КАК Связи
ГДЕ
    Связи.СвязьВверх = ЗНАЧЕНИЕ(Справочник.Связи.ПустаяСсылка)

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

ВЫБРАТЬ
    Связи.Ссылка,
    Связи.Заявка,
    Рекурсия.Уровень + 1
ИЗ
    Справочник.Связи КАК Связи
Присоединить Рекурсия ПО Связи.СвязьВверх = Рекурсия.Ссылка

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

Решение 3: Визуализация через иерархию (Дерево)

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

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

Решение 4: Работа с Таблицей Значений на клиенте

В ситуациях, когда данных не слишком много (несколько сотен строк) и требуется активное интерактивное перемещение (Drag-and-Drop), наиболее комфортным для пользователя будет использование Таблицы Значений (ТЗ) вместо динамического списка. Посмотрим на алгоритм реализации:

  1. При открытии формы выполняем запрос, который выгружает все связи.
  2. В коде на встроенном языке (через цикл или рекурсию) выстраиваем правильный порядок элементов в объекте ТаблицаЗначений.
  3. Загружаем готовую таблицу в реквизит формы.
  4. При перемещении строки пользователем (события ПроверкаПеретаскивания и Перетаскивание), мы просто меняем строки в ТЗ и обновляем ссылки СвязьВверх/СвязьВниз в базе данных одним пакетом.

Пример кода для выстраивания цепочки в таблицу:


// Сначала найдем корень
ТекущийЭлемент = Справочники.Связи.НайтиПоРеквизиту("СвязьВверх", Справочники.Связи.ПустаяСсылка);

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

Подведение итогов и выбор метода

Мы проанализировали несколько подходов к решению задачи сортировки по двусвязному списку. Подведем итоги:

Метод с числовым порядком — самый универсальный. Он позволяет использовать стандартный ДинамическийСписок с его мощным функционалом отборов и поиска. Единственная сложность — необходимость логики пересчета весов при перетаскивании.

Рекурсивные запросы — наиболее современный и «чистый» с точки зрения архитектуры данных метод, доступный в новых версиях 1С. Он исключает избыточность данных (не нужен реквизит Порядок).

Таблица значений — лучший выбор для небольших списков с интенсивным редактированием порядка, так как она обеспечивает максимальную отзывчивость интерфейса при операциях Drag-and-Drop.

Выбор конкретного метода зависит от версии вашей платформы и ожидаемого объема данных в справочнике Связи.

← На главную