Как отсортировать номенклатуру в табличной части документа по данным из внешнего файла

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

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

Метод 1: Использование вспомогательной колонки в Таблице Значений

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

Для начала проанализируем ситуацию: у нас есть таблица данных из Excel (назовем ее ТаблицаДанныхЭксель) и табличная часть документа Товары. Нам нужно, чтобы товары, найденные в Excel, имели приоритет при сортировке.

Выполним следующие действия:

  1. Выгрузим табличную часть документа в промежуточную таблицу значений.
  2. Добавим в эту таблицу вспомогательную колонку для индекса сортировки.
  3. Заполним этот индекс на основании наличия номенклатуры в данных из Excel.
  4. Произведем сортировку и загрузим данные обратно.

Пример программного кода для реализации этого алгоритма:


// 1. Выгружаем ТЧ документа в ТЗ
ТЗ_Товары = Объект.Товары.Выгрузить();

// 2. Добавляем колонку для признака сортировки
// Используем тип "Число", чтобы иметь возможность гибко управлять порядком
ТЗ_Товары.Колонки.Добавить("ПорядокСортировки", Новый ОписаниеТипов("Число"));

// 3. Обходим строки и расставляем приоритеты
Для Каждого СтрокаТЧ Из ТЗ_Товары Цикл
    // Ищем номенклатуру в таблице, полученной из Excel
    СтрокаЭксель = ТаблицаДанныхЭксель.Найти(СтрокаТЧ.Номенклатура, "НоменклатураВБазе");
    
    Если СтрокаЭксель <> Неопределено Тогда
        // Если нашли — ставим высокий приоритет (меньшее число)
        СтрокаТЧ.ПорядокСортировки = 1;
    Иначе
        // Если не нашли — ставим низкий приоритет
        СтрокаТЧ.ПорядокСортировки = 2;
    КонецЕсли;
КонецЦикла;

// 4. Сортируем по вспомогательной колонке и, при необходимости, по наименованию
ТЗ_Товары.Сортировать("ПорядокСортировки Возр, Номенклатура Наименовани");

// 5. Загружаем отсортированные данные обратно в документ
Объект.Товары.Загрузить(ТЗ_Товары);

Метод 2: Сохранение точного порядка «как в Excel»

Иногда простого разделения на «есть в файле» и «нет в файле» недостаточно. Заказчик может потребовать, чтобы товары в документе располагались ровно в той последовательности, в которой они записаны в Excel-файле. Для этого нам понадобится более детальный индекс.

Проанализируем ситуацию: если в Excel первым идет «Болт», вторым — «Гайка», а в документе они были в обратном порядке, то после сортировки «Болт» должен стать первой строкой документа.

Для реализации этого мы добавим в таблицу из Excel колонку НомерСтрокиЭксель. Посмотрим, как это будет выглядеть в коде:


// Добавляем индекс в таблицу данных Excel
ТаблицаДанныхЭксель.Колонки.Добавить("ИндексФайла", Новый ОписаниеТипов("Число"));
Для Счетчик = 0 По ТаблицаДанныхЭксель.Количество() - 1 Цикл
    ТаблицаДанныхЭксель[Счетчик].ИндексФайла = Счетчик;
КонецЦикла;

// Теперь при обработке ТЧ документа присваиваем этот индекс строкам
ТЗ_Товары = Объект.Товары.Выгрузить();
ТЗ_Товары.Колонки.Добавить("ПорядокСортировки", Новый ОписаниеТипов("Число"));

Для Каждого СтрокаТЧ Из ТЗ_Товары Цикл
    СтрокаЭксель = ТаблицаДанныхЭксель.Найти(СтрокаТЧ.Номенклатура, "НоменклатураВБазе");
    Если СтрокаЭксель <> Неопределено Тогда
        // Присваиваем индекс из файла
        СтрокаТЧ.ПорядокСортировки = СтрокаЭксель.ИндексФайла;
    Иначе
        // Для позиций, которых нет в файле, задаем индекс заведомо больше любого возможного в файле
        СтрокаТЧ.ПорядокСортировки = 999999;
    КонецЕсли;
КонецЦикла;

ТЗ_Товары.Сортировать("ПорядокСортировки Возр");
Объект.Товары.Загрузить(ТЗ_Товары);

Метод 3: Использование запроса (для продвинутых сценариев)

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

Рассмотрим правильный подход к использованию запроса:

Выясним причину, почему метод Загрузить() может быть опасен. Если ваша ТЧ содержит 20 колонок, а в запросе вы выбрали только Номенклатура и Количество, то после выполнения команды Объект.Товары.Загрузить(РезультатЗапроса) все остальные 18 колонок (Цена, Сумма, Ставка НДС и т.д.) станут пустыми. Чтобы этого избежать, необходимо выбирать в запросе все колонки таблицы части.

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


Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;

// Помещаем таблицу документа во временную таблицу (ВТ)
// Важно: ТЗ_Товары должна содержать все колонки ТЧ!
Запрос.УстановитьПараметр("ТЗ_Товары", ТЗ_Товары);
Запрос.Текст = 
"ВЫБРАТЬ * ПОМЕСТИТЬ ВТ_Товары ИЗ &ТЗ_Товары КАК ТЗ_Товары;
|/////////////////////////////////////////////////
|ВЫБРАТЬ * ПОМЕСТИТЬ ВТ_Эксель ИЗ &ТаблицаДанныхЭксель КАК ТаблицаДанныхЭксель;
|/////////////////////////////////////////////////
|ВЫБРАТЬ
|   ВТ_Товары.*
|ИЗ
|   ВТ_Товары КАК ВТ_Товары
|   ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Эксель КАК ВТ_Эксель
|   ПО ВТ_Товары.Номенклатура = ВТ_Эксель.НоменклатураВБазе
|УПОРЯДОЧИТЬ ПО
|   ЕСТЬNULL(ВТ_Эксель.ИндексФайла, 999999) ВОЗР";

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

Важные технические нюансы

При реализации сортировки важно помнить о нескольких критических моментах:

  1. Уникальные идентификаторы строк: Использование метода Загрузить() полностью перезаписывает строки. Если в вашей конфигурации реализована сложная логика, привязанная к идентификаторам строк (например, связь основной ТЧ с ТЧ акцизов или серийных номеров), то после такой «сортировки» связи могут разорваться. В таких случаях вместо Загрузить() следует использовать метод Сдвинуть(), перемещая строки в цикле на нужные позиции.
  2. Дублирование позиций: Если в документе одна и та же номенклатура встречается в двух строках (например, с разной ценой), а в Excel она указана один раз, обычное ЛЕВОЕ СОЕДИНЕНИЕ в запросе может привести к дублированию или некорректной сортировке. Чтобы этого избежать, всегда добавляйте в ТЗ документа колонку КлючСвязи (уникальный номер строки), по которому и делайте соединение в запросе.
  3. Типизация колонок: При передаче ТаблицыЗначений в запрос убедитесь, что колонки имеют строго определенные типы. Если колонка Номенклатура будет иметь тип «Произвольный», запрос может выдать ошибку при выполнении соединения на стороне СУБД.

Подведем итог: для большинства задач оптимальным решением является использование ТаблицыЗначений со вспомогательной колонкой-индексом. Это прозрачный, легко отлаживаемый код, который минимизирует риск потери данных и не создает избыточной нагрузки на сервер базы данных.

← На главную