Как правильно отсортировать табличную часть документа по иерархии справочника "Номенклатура" в 1С 8.3?

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

Мы часто сталкиваемся с задачей упорядочивания данных в табличных частях документов таким образом, чтобы это было удобно для работы. Один из распространенных сценариев – сортировка строк по иерархии справочника, например, чтобы товары в документе следовали порядку их расположения на складе. Давайте разберем, как эффективно решить эту проблему на примере документа "Реализация товаров и услуг" и его табличной части "Товары" в конфигурации 1С:Управление торговлей 11.5.

Анализируем исходную проблему и предлагаем первое решение: коррекция запроса

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


&НаСервере
Процедура Сортировка()
    Этотобъект.Записать(); // Важно: сохраняем документ перед выполнением запроса

    Запрос = Новый Запрос;
    Запрос.УстановитьПараметр("Ссылка", Объект.Ссылка);

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

    Результат = Запрос.Выполнить();
    Товар = Результат.Выгрузить();
    Объект.Товары.Загрузить(Товар);
КонецПроцедуры

Давайте внимательно рассмотрим строку УПОРЯДОЧИТЬ ПО Номенклатура ИЕРАРХИЯ. На первый взгляд кажется, что это именно то, что нам нужно. Однако, на практике такой подход сортирует строки не по визуальному положению элементов в иерархии справочника, а по внутреннему представлению ссылки на элемент номенклатуры. Это означает, что группы и элементы будут упорядочены по их уникальным идентификаторам, что редко совпадает с желаемым логическим или алфавитным порядком в иерархии.

Чтобы добиться истинной иерархической сортировки, которая учитывает порядок групп и элементов так, как мы их видим в справочнике (например, по наименованию), нам необходимо указать поле наименования справочника в условии упорядочивания. Таким образом, запрос будет сортировать по наименованию, следуя иерархической структуре.

Предлагаем следующее корректирующее решение для запроса:


&НаСервере
Процедура Сортировка()
    Этотобъект.Записать(); // Сохраняем документ

    Запрос = Новый Запрос;
    Запрос.УстановитьПараметр("Ссылка", Объект.Ссылка);

    Запрос.Текст = 
    "ВЫБРАТЬ
    |    РеализацияТоваровУслугТовары.* // Выбираем все поля табличной части
    |ИЗ
    |    Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары
    |ГДЕ
    |    РеализацияТоваровУслугТовары.Ссылка = &Ссылка
    |
    |УПОРЯДОЧИТЬ ПО  
    |РеализацияТоваровУслугТовары.Номенклатура.Наименование ИЕРАРХИЯ";

    Результат = Запрос.Выполнить();
    // Получаем ТаблицуЗначений из результата запроса
    ТаблицаТоваров = Результат.Выгрузить(); 
    // Загружаем отсортированные данные обратно в табличную часть
    Объект.Товары.Загрузить(ТаблицаТоваров);
КонецПроцедуры

Обратите внимание на ключевое изменение: УПОРЯДОЧИТЬ ПО РеализацияТоваровУслугТовары.Номенклатура.Наименование ИЕРАРХИЯ. Теперь система будет использовать наименование номенклатуры для построения иерархического порядка, что соответствует нашим ожиданиям, например, для удобства сборки товаров на складе.

Мы также изменили выбор полей на РеализацияТоваровУслугТовары.*. Это очень важный момент. Использование символа * позволяет выбрать все существующие реквизиты табличной части документа. Если в будущем в конфигурацию будут добавлены новые реквизиты в табличную часть "Товары", наш запрос автоматически включит их в выборку. В противном случае, если мы явно перечислим поля, а затем добавим новые, эти новые поля могут быть обнулены или потеряны при обратной загрузке данных методом Загрузить(). Хотя ваш исходный запрос перечислял почти все поля, использование * делает код более устойчивым к изменениям конфигурации.

Методы, которые мы используем здесь:

Альтернативное решение: сортировка по дополнительному реквизиту номенклатуры

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

Рассмотрим по шагам, как реализовать этот подход:

  1. Создание дополнительного реквизита: Мы добавляем новый реквизит в справочник Номенклатура, например, с именем ПорядокНаСкладе. Этот реквизит может быть числового типа, строкового или даже ссылочного, в зависимости от сложности нашей логики сортировки. Например, для простой нумерации подойдет числовой тип.
  2. Заполнение реквизита: Мы вручную или программно заполняем этот реквизит для каждой позиции номенклатуры, указывая желаемый порядок. Например, для группы "Молочные продукты" мы можем указать 1, для "Хлебобулочные изделия" - 2, и так далее, а внутри групп также пронумеровать элементы.
  3. Изменение запроса: Теперь мы можем использовать этот новый реквизит в условии УПОРЯДОЧИТЬ ПО нашего запроса.

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


&НаСервере
Процедура СортировкаПоПорядкуНаСкладе()
    Этотобъект.Записать(); // Сохраняем документ

    Запрос = Новый Запрос;
    Запрос.УстановитьПараметр("Ссылка", Объект.Ссылка);

    Запрос.Текст = 
    "ВЫБРАТЬ
    |    РеализацияТоваровУслугТовары.*,
    |    РеализацияТоваровУслугТовары.Номенклатура.ПорядокНаСкладе КАК ПорядокНаСкладе // Выбираем новый реквизит
    |ИЗ
    |    Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары
    |ГДЕ
    |    РеализацияТоваровУслугТовары.Ссылка = &Ссылка
    |
    |УПОРЯДОЧИТЬ ПО  
    |ПорядокНаСкладе,
    |РеализацияТоваровУслугТовары.Номенклатура.Наименование ИЕРАРХИЯ"; // Дополнительная сортировка по наименованию
                                                                        // внутри одного значения ПорядокНаСкладе

    Результат = Запрос.Выполнить();
    ТаблицаТоваров = Результат.Выгрузить(); 
    Объект.Товары.Загрузить(ТаблицаТоваров);
КонецПроцедуры

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

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

Ограничения и важные моменты

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

Не забывайте о строке Этотобъект.Записать(); в начале процедуры. Если вы вносите какие-либо изменения в документ до выполнения сортировки (например, добавляете новые строки или меняете существующие), обязательно сохраните документ перед тем, как выполнять запрос. Запрос всегда работает с данными, которые были записаны в базу данных, а не с данными, которые находятся только в оперативной памяти формы.

В итоге, для большинства задач, связанных с иерархической сортировкой табличной части по справочнику, наиболее эффективным и надежным решением является изменение запроса с УПОРЯДОЧИТЬ ПО Номенклатура ИЕРАРХИЯ на УПОРЯДОЧИТЬ ПО Номенклатура.Наименование ИЕРАРХИЯ. Если же требуется более сложная логика, использование дополнительного реквизита в справочнике и его включение в условие сортировки запроса предоставит необходимую гибкость.

← К списку