В процессе разработки часто возникает задача синхронизации данных документа с внешними источниками, например, файлами Excel. Типичная ситуация: табличная часть (ТЧ) документа уже заполнена, но после загрузки данных извне необходимо изменить порядок строк так, чтобы определенные позиции оказались вверху списка в строго заданном порядке, а остальные остались ниже. Разберем, как реализовать такую сортировку в 1С эффективно и безопасно.
Самый простой и надежный способ изменить порядок строк в табличной части — это работа с объектом ТаблицаЗначений. Основная идея заключается в том, чтобы выгрузить данные ТЧ, добавить в них признак, по которому будет производиться сортировка, и загрузить результат обратно. Рассмотрим этот процесс по шагам.
Для начала проанализируем ситуацию: у нас есть таблица данных из Excel (назовем ее ТаблицаДанныхЭксель) и табличная часть документа Товары. Нам нужно, чтобы товары, найденные в Excel, имели приоритет при сортировке.
Выполним следующие действия:
Пример программного кода для реализации этого алгоритма:
// 1. Выгружаем ТЧ документа в ТЗ
ТЗ_Товары = Объект.Товары.Выгрузить();
// 2. Добавляем колонку для признака сортировки
// Используем тип "Число", чтобы иметь возможность гибко управлять порядком
ТЗ_Товары.Колонки.Добавить("ПорядокСортировки", Новый ОписаниеТипов("Число"));
// 3. Обходим строки и расставляем приоритеты
Для Каждого СтрокаТЧ Из ТЗ_Товары Цикл
// Ищем номенклатуру в таблице, полученной из Excel
СтрокаЭксель = ТаблицаДанныхЭксель.Найти(СтрокаТЧ.Номенклатура, "НоменклатураВБазе");
Если СтрокаЭксель <> Неопределено Тогда
// Если нашли — ставим высокий приоритет (меньшее число)
СтрокаТЧ.ПорядокСортировки = 1;
Иначе
// Если не нашли — ставим низкий приоритет
СтрокаТЧ.ПорядокСортировки = 2;
КонецЕсли;
КонецЦикла;
// 4. Сортируем по вспомогательной колонке и, при необходимости, по наименованию
ТЗ_Товары.Сортировать("ПорядокСортировки Возр, Номенклатура Наименовани");
// 5. Загружаем отсортированные данные обратно в документ
Объект.Товары.Загрузить(ТЗ_Товары);
Иногда простого разделения на «есть в файле» и «нет в файле» недостаточно. Заказчик может потребовать, чтобы товары в документе располагались ровно в той последовательности, в которой они записаны в Excel-файле. Для этого нам понадобится более детальный индекс.
Проанализируем ситуацию: если в Excel первым идет «Болт», вторым — «Гайка», а в документе они были в обратном порядке, то после сортировки «Болт» должен стать первой строкой документа.
Для реализации этого мы добавим в таблицу из Excel колонку НомерСтрокиЭксель. Посмотрим, как это будет выглядеть в коде:
// Добавляем индекс в таблицу данных Excel
ТаблицаДанныхЭксель.Колонки.Добавить("ИндексФайла", Новый ОписаниеТипов("Число"));
Для Счетчик = 0 По ТаблицаДанныхЭксель.Количество() - 1 Цикл
ТаблицаДанныхЭксель[Счетчик].ИндексФайла = Счетчик;
КонецЦикла;
// Теперь при обработке ТЧ документа присваиваем этот индекс строкам
ТЗ_Товары = Объект.Товары.Выгрузить();
ТЗ_Товары.Колонки.Добавить("ПорядокСортировки", Новый ОписаниеТипов("Число"));
Для Каждого СтрокаТЧ Из ТЗ_Товары Цикл
СтрокаЭксель = ТаблицаДанныхЭксель.Найти(СтрокаТЧ.Номенклатура, "НоменклатураВБазе");
Если СтрокаЭксель <> Неопределено Тогда
// Присваиваем индекс из файла
СтрокаТЧ.ПорядокСортировки = СтрокаЭксель.ИндексФайла;
Иначе
// Для позиций, которых нет в файле, задаем индекс заведомо больше любого возможного в файле
СтрокаТЧ.ПорядокСортировки = 999999;
КонецЕсли;
КонецЦикла;
ТЗ_Товары.Сортировать("ПорядокСортировки Возр");
Объект.Товары.Загрузить(ТЗ_Товары);
Использование СУБД для сортировки двух таблиц значений оправдано, когда данных очень много или требуются сложные соединения (например, учет характеристик, серий и складов одновременно). Однако здесь кроется ловушка, о которой упоминалось в обсуждении на форуме: при загрузке результата запроса методом Загрузить() можно потерять данные в тех колонках, которые не были выбраны в запросе.
Рассмотрим правильный подход к использованию запроса:
Выясним причину, почему метод Загрузить() может быть опасен. Если ваша ТЧ содержит 20 колонок, а в запросе вы выбрали только Номенклатура и Количество, то после выполнения команды Объект.Товары.Загрузить(РезультатЗапроса) все остальные 18 колонок (Цена, Сумма, Ставка НДС и т.д.) станут пустыми. Чтобы этого избежать, необходимо выбирать в запросе все колонки таблицы части.
Пример безопасного запроса для сортировки:
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
// Помещаем таблицу документа во временную таблицу (ВТ)
// Важно: ТЗ_Товары должна содержать все колонки ТЧ!
Запрос.УстановитьПараметр("ТЗ_Товары", ТЗ_Товары);
Запрос.Текст =
"ВЫБРАТЬ * ПОМЕСТИТЬ ВТ_Товары ИЗ &ТЗ_Товары КАК ТЗ_Товары;
|/////////////////////////////////////////////////
|ВЫБРАТЬ * ПОМЕСТИТЬ ВТ_Эксель ИЗ &ТаблицаДанныхЭксель КАК ТаблицаДанныхЭксель;
|/////////////////////////////////////////////////
|ВЫБРАТЬ
| ВТ_Товары.*
|ИЗ
| ВТ_Товары КАК ВТ_Товары
| ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Эксель КАК ВТ_Эксель
| ПО ВТ_Товары.Номенклатура = ВТ_Эксель.НоменклатураВБазе
|УПОРЯДОЧИТЬ ПО
| ЕСТЬNULL(ВТ_Эксель.ИндексФайла, 999999) ВОЗР";
Результат = Запрос.Выполнить().Выгрузить();
Объект.Товары.Загрузить(Результат);
При реализации сортировки важно помнить о нескольких критических моментах:
Загрузить() полностью перезаписывает строки. Если в вашей конфигурации реализована сложная логика, привязанная к идентификаторам строк (например, связь основной ТЧ с ТЧ акцизов или серийных номеров), то после такой «сортировки» связи могут разорваться. В таких случаях вместо Загрузить() следует использовать метод Сдвинуть(), перемещая строки в цикле на нужные позиции.ЛЕВОЕ СОЕДИНЕНИЕ в запросе может привести к дублированию или некорректной сортировке. Чтобы этого избежать, всегда добавляйте в ТЗ документа колонку КлючСвязи (уникальный номер строки), по которому и делайте соединение в запросе.ТаблицыЗначений в запрос убедитесь, что колонки имеют строго определенные типы. Если колонка Номенклатура будет иметь тип «Произвольный», запрос может выдать ошибку при выполнении соединения на стороне СУБД.Подведем итог: для большинства задач оптимальным решением является использование ТаблицыЗначений со вспомогательной колонкой-индексом. Это прозрачный, легко отлаживаемый код, который минимизирует риск потери данных и не создает избыточной нагрузки на сервер базы данных.