В практике разработки часто встречается требование от складских работников или менеджеров: "Сделайте так, чтобы товары в документе шли по порядку папок в справочнике". Это кажется логичным для сборки заказа. Однако при реализации этой задачи начинающие разработчики часто совершают две ошибки: используют неправильную конструкцию запроса и применяют опасные методы перезаписи данных. Разберем эту ситуацию подробно, проанализируем ошибки и найдем верные решения.
Рассмотрим типичный код, который вызывает вопросы. Разработчик пытается получить таблицу товаров, отсортировать её запросом и загрузить обратно в документ.
// Пример ошибочного подхода
Запрос.Текст =
"ВЫБРАТЬ
| РеализацияТоваровУслугТовары.Ссылка,
| ... // перечисление всех полей
|ИЗ
| Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары
|ГДЕ
| РеализацияТоваровУслугТовары.Ссылка = &Ссылка
|
|УПОРЯДОЧИТЬ ПО
|Номенклатура ИЕРАРХИЯ"; // Ошибка логики сортировки
Результат = Запрос.Выполнить();
Товар = Результат.Выгрузить();
Объект.Товары.Загрузить(Товар); // Критическая ошибка безопасности данных
Давайте разберем, что здесь не так.
Конструкция УПОРЯДОЧИТЬ ПО Номенклатура ИЕРАРХИЯ предназначена для построения древовидных структур (обычно вместе с итогами). Когда мы применяем её к плоской выборке (только элементы, без групп), платформа пытается сгруппировать записи по родителям. Но так как родителей в выборке нет, визуально сортировка может выглядеть хаотично или происходить по внутренним идентификаторам, а не по алфавиту папок.
Решение: Если нам нужно, чтобы сначала шли товары из папки "А", потом из папки "Б", нам нужно явно указать это в сортировке, используя разыменование полей.
УПОРЯДОЧИТЬ ПО
Номенклатура.Родитель.Наименование,
Номенклатура.Наименование
Если уровень вложенности папок большой, такой подход тоже может быть не идеален. В этом случае правильнее использовать механизмы СКД или специализированные алгоритмы с левым соединением справочника самого с собой, но для простых задач сортировки по "основной папке" вышеуказанного кода обычно достаточно.
Использование метода Объект.Товары.Загрузить(Товар) — это "мина замедленного действия". Рассмотрим почему:
Загрузить() полностью очищает текущую таблицу и заполняет её данными из вашей таблицы значений.Правильный подход: Вместо полной перезагрузки данных, мы должны менять порядок строк в существующей таблице или сортировать таблицу значений и загружать её только если мы контролируем все поля (например, во временной структуре).
Давайте реализуем безопасный механизм сортировки. Мы не будем перезаписывать данные, мы просто поменяем их порядок с помощью индексов или метода Сдвинуть, либо воспользуемся сортировкой таблицы значений с последующим сопоставлением.
Самый простой и безопасный способ для управляемых форм — выгрузить данные в таблицу значений, добавить колонку для сортировки, отсортировать и загрузить обратно, но с учетом всех колонок. Однако, чтобы избежать потери данных, лучше использовать метод сортировки самой табличной части (если это возможно на клиенте) или переупорядочивание через индексы.
Рассмотрим вариант с сортировкой через служебную таблицу значений:
&НаСервере
Процедура СортироватьТоварыНаСервере()
// 1. Выгружаем табличную часть в Таблицу Значений
ТаблицаТоваров = Объект.Товары.Выгрузить();
// 2. Добавляем колонки, которых нет в ТЧ, но нужны для сортировки
ТаблицаТоваров.Колонки.Добавить("НаименованиеРодителя", Новый ОписаниеТипов("Строка"));
// 3. Заполняем данные для сортировки
// Для оптимизации лучше сделать это одним запросом к базе, передав массив номенклатуры
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Номенклатура,
| Номенклатура.Родитель.Наименование КАК НаименованиеРодителя
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Ссылка В (&МассивНоменклатуры)";
МассивНоменклатуры = ТаблицаТоваров.ВыгрузитьКолонку("Номенклатура");
Запрос.УстановитьПараметр("МассивНоменклатуры", МассивНоменклатуры);
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
НайденныеСтроки = ТаблицаТоваров.НайтиСтроки(Новый Структура("Номенклатура", Выборка.Номенклатура));
Для Каждого СтрокаТЗ Из НайденныеСтроки Цикл
СтрокаТЗ.НаименованиеРодителя = Выборка.НаименованиеРодителя;
КонецЦикла;
КонецЦикла;
// 4. Сортируем таблицу значений
ТаблицаТоваров.Сортировать("НаименованиеРодителя, Номенклатура");
// 5. Загружаем обратно.
// ВАЖНО: Метод Загрузить здесь безопасен ТОЛЬКО если ТаблицаТоваров была получена методом Выгрузить() из ТОЙ ЖЕ ТЧ
// и мы не удаляли колонки.
Объект.Товары.Загрузить(ТаблицаТоваров);
КонецПроцедуры
Часто менять порядок строк в самом документе нецелесообразно. Менеджеры привыкли видеть товары в хронологическом порядке добавления. Сортировка по иерархии нужна в основном кладовщикам для удобства сбора заказа.
В этом случае наиболее профессиональным решением будет оставить порядок строк в документе без изменений, а сортировку применить только при формировании печатной формы или в рабочем месте кладовщика.
Если же цель сортировки — упростить набор на складе, стоит обратить внимание на механизм ячеистого хранения. В современных конфигурациях (УТ 11, ERP) система сама выстраивает маршрут сборщика по ячейкам ("змейкой"), игнорируя иерархию папок номенклатуры, так как товары из одной группы могут физически лежать в разных концах склада.