При разработке интерфейсов в 1С мы часто сталкиваемся с задачей удобного отображения списков документов. Стандартная нумерация в платформе обычно включает текстовый префикс (например, "УТ-00001" или "БП00-0023"). Для системы это строка, и сортировка происходит лексикографически (посимвольно). Однако пользователи часто просят сортировать список только по числовой части, игнорируя буквы организации или базы. Разберем подробно, почему простые методы не работают и как реализовать надежное архитектурное решение.
Давайте проанализируем, как 1С (и SQL-сервер под капотом) сортирует строки. Допустим, у нас есть номера:
Если мы включим стандартную сортировку по полю Номер, документы выстроятся по алфавиту префиксов:
Пользователю же нужно видеть их в хронологическом порядке создания номеров: 1, 2, 3. Задача осложняется тем, что форматы номеров могут быть "грязными" (разное количество нулей, наличие слэшей, ручной ввод).
В обсуждении часто возникают идеи решить проблему "в лоб". Давайте разберем, почему мы не должны так делать, если хотим построить производительную систему.
1. Ошибка выгрузки в Таблицу Значений
Первая мысль новичка: "Я выгружу динамический список в Таблицу Значений, отсортирую её кодом и загружу обратно".
Почему это плохая идея:
2. Вычисляемые поля в запросе
Второй популярный подход — написать хитрый запрос в тексте динамического списка. Использовать функции вроде ПОДСТРОКА, НАЙТИ или ВЫРАЗИТЬ.
Сложности этого метода:
ПОДСТРОКА(Номер, 5, 10), серверу придется перебрать каждую строку таблицы документов, вычислить значение и только потом сортировать. На больших объемах это приведет к критическим тормозам при открытии списка.Самый надежный и быстрый способ (стандарт де-факто в высоконагруженных системах) — это нормализация данных при записи. Мы создадим специальное поле, в котором будет храниться "чистый" номер в формате, пригодном для сортировки, и построим индекс по этому полю.
Добавим в документ новый реквизит. Назовем его, например, НомерДляСортировки (или Порядок).
Индексировать (или "Индексировать с допупорядочиванием"). Это обеспечит мгновенную сортировку списка.Важно: Почему лучше выбрать тип Строка, а не Число?
Если ваши номера всегда строго числовые (например, "123"), тип Число подойдет. Но на практике встречаются номера со слэшами ("123/1"), буквами в суффиксах или ведущими нулями. Строка универсальнее.
Чтобы строковая сортировка работала как числовая (чтобы "2" не было больше "10"), мы должны использовать выравнивание нулями (padding). Строка "000000002" будет корректно стоять перед "000000010".
Теперь нам нужно, чтобы при каждом сохранении документа наш служебный реквизит обновлялся автоматически. Для этого используем подписку на событие или обработчик ПередЗаписью в модуле объекта.
Мы будем использовать функцию общего модуля БСП ПрефиксацияОбъектовКлиентСервер.НомерНаПечать для очистки от префиксов, либо напишем свою логику.
Рассмотрим пример кода для модуля объекта документа:
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
// Получаем "чистый" номер, используя типовую функцию БСП
// Параметры: НомерОбъекта, УдалитьПрефиксИБ, УдалитьПользовательскийПрефикс
ЧистыйНомер = ПрефиксацияОбъектовКлиентСервер.НомерНаПечать(ЭтотОбъект.Номер, Истина, Истина);
// Если БСП нет, можно использовать простую логику обрезки,
// но это менее надежно для сложных префиксов.
// НОРМАЛИЗАЦИЯ:
// Приводим к единому формату с ведущими нулями для корректной сортировки строк.
// Допустим, мы хотим длину 20 знаков.
// Удаляем все нецифровые символы (если нужно совсем строго),
// либо оставляем как есть, но дополняем нулями слева.
// Пример простой нормализации (удаление пробелов и дополнение нулями):
ЧистыйНомер = СокрЛП(ЧистыйНомер);
// Функция прав() возьмет правую часть строки, дополненной нулями слева
// "0000000000" + "123" -> ...000123
ПолеСортировки = Прав("00000000000000000000" + ЧистыйНомер, 20);
// Записываем в наш служебный реквизит
ЭтотОбъект.НомерДляСортировки = ПолеСортировки;
КонецПроцедуры
В теме обсуждалась проблема, когда номера содержат разделители, например 112/1. Если просто удалить префикс, мы получим строку "112/1". При сортировке это может дать неожиданный результат по сравнению с "112".
Чтобы решить эту проблему, давайте усовершенствуем алгоритм нормализации. Лучший подход — разделить номер на две части: "Основной номер" и "Номер исправления/дроби", нормализовать их отдельно и склеить.
Рассмотрим алгоритм для номера СП-00123/5:
00123/5.0000000123.00005.0000000123.00005.Такая строка будет идеально сортироваться в базе данных.
// Пример функции нормализации сложного номера
Функция ПолучитьНормализованныйНомер(Знач ВходнойНомер)
// 1. Убираем префиксы (используем стандартную функцию или свою логику)
// Предположим, мы получили "123/5"
СтрокаБезПрефикса = ПрефиксацияОбъектовКлиентСервер.НомерНаПечать(ВходнойНомер, Истина, Истина);
// 2. Ищем разделитель дроби
ПозицияСлэша = СтрНайти(СтрокаБезПрефикса, "/");
Если ПозицияСлэша > 0 Тогда
ОсновнаяЧасть = Лев(СтрокаБезПрефикса, ПозицияСлэша - 1);
ДробнаяЧасть = Сред(СтрокаБезПрефикса, ПозицияСлэша + 1);
Иначе
ОсновнаяЧасть = СтрокаБезПрефикса;
ДробнаяЧасть = "0";
КонецЕсли;
// 3. Убираем все лишнее (например, пробелы) и дополняем нулями
// Используем "Прав", чтобы получить строку фиксированной длины с ведущими нулями
НормаОснова = Прав("000000000000" + СокрЛП(ОсновнаяЧасть), 12);
НормаДробь = Прав("00000" + СокрЛП(ДробнаяЧасть), 5);
// Результат будет вида "000000000123.00005"
Возврат НормаОснова + "." + НормаДробь;
КонецФункции
После добавления реквизита и кода в модуль, новые документы будут сохраняться с заполненным полем сортировки. Однако старые документы останутся с пустым реквизитом.
Нам необходимо написать внешнюю обработку, которая:
Рекомендуем выполнять эту операцию в монопольном режиме или порциями, чтобы не блокировать работу пользователей.
Финальный шаг — настройка формы списка.
Дата или Номер.НомерДляСортировки (по возрастанию или убыванию).НомерДляСортировки выводить на форму пользователю не обязательно, сортировка будет работать и без визуального отображения колонки.Мы выяснили, что для корректной сортировки "без префиксов" нельзя полагаться на динамическую обработку строк в момент открытия формы. Правильный подход включает:
Это решение гарантирует высокую производительность и гибкость даже при наличии сложной нумерации с дробями.