В стандартной логике платформы 1С:Предприятие механизмы нумерации объектов (справочников, документов) настроены на автоматическое увеличение номера. Однако в практике разработки иногда возникают специфические задачи, когда необходимо обеспечить «обратный отсчет» — чтобы каждый новый элемент получал код меньше предыдущего (например, от 999 к 001). Рассмотрим подробнее, как это можно реализовать, какие технические сложности могут возникнуть и почему стандартные механизмы могут сопротивляться такому подходу.
Прежде чем переходить к коду, выясним причину, по которой 1С не поддерживает убывающую нумерацию «из коробки». Платформа при создании нового объекта выполняет поиск максимального кода в базе данных, анализирует его числовую часть и прибавляет к ней единицу. Если мы вручную установим код 999, следующий элемент по логике системы должен получить код 1000. Нам же требуется переопределить это поведение.
Проанализируем ситуацию с точки зрения типов данных. Код справочника чаще всего имеет тип Строка. Это означает, что сортировка и поиск «максимального» значения происходят по правилам строкового сравнения. Чтобы наша обратная нумерация работала корректно, критически важно использовать ведущие нули. Без них код «9» будет считаться больше, чем «100», что полностью нарушит логику автоподбора.
Наиболее правильным программным способом вмешательства в нумерацию является использование обработчика события ПриУстановкеНовогоНомера. Это событие вызывается платформой в тот момент, когда объект запрашивает новый номер, но еще не успел его записать. Разберем по шагам алгоритм действий в этом методе:
СтандартнаяОбработка = Ложь.Посмотрим на пример кода, который можно разместить в модуле объекта справочника или в подписке на событие:
Процедура ПриУстановкеНовогоНомера(СтандартнаяОбработка, Префикс)
СтандартнаяОбработка = Ложь; // Отключаем встроенный механизм
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| МИНИМУМ(Спр.Код) КАК МинКод
|ИЗ
| Справочник.ИмяВашегоСправочника КАК Спр";
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Если Выборка.Следующий() И ЗначениеЗаполнено(Выборка.МинКод) Тогда
// Преобразуем код в число, учитывая возможные префиксы
ЧисловойКод = Число(Выборка.МинКод);
НовыйЧисловойКод = ЧисловойКод - 1;
// Формируем строку с ведущими нулями (например, для длины 9)
ПараметрыФормата = "ЧЦ=9; ЧВН=; ЧГ=0";
КодСтрокой = Формат(НовыйЧисловойКод, ПараметрыФормата);
ЭтотОбъект.Код = КодСтрокой;
Иначе
// Если элементов еще нет, устанавливаем стартовый максимальный код
ЭтотОбъект.Код = "000000999";
КонецЕсли;
КонецПроцедуры
Рассмотрим ситуацию, когда два пользователя одновременно создают новый элемент. Оба запроса МИНИМУМ(Код) могут вернуть одно и то же число, в результате чего система попытается записать два элемента с одинаковыми кодами, что вызовет ошибку уникальности. Чтобы этого избежать, необходимо использовать механизм объектных блокировок.
Выясним причину необходимости блокировок: запрос на получение минимального кода должен быть защищен от чтения другими сеансами до момента завершения транзакции записи. В управляемом приложении 1С 8.3 рекомендуется использовать объект БлокировкаДанных. Мы устанавливаем исключительную блокировку на весь справочник или на конкретный узел, чтобы гарантировать, что номер, который мы «вычислили», не будет занят кем-то другим в ту же секунду.
Для повышения производительности, особенно в высоконагруженных системах, использование запроса ВЫБРАТЬ МИНИМУМ к большой таблице справочника может быть неэффективным. Разберем альтернативный подход: создание непериодического регистра сведений, который будет хранить только одну запись — «ПоследнийВыданныйКод».
При таком подходе процесс выглядит следующим образом:
Это позволяет избежать сканирования всей таблицы справочника и делает работу с номерами атомарной.
Часто бизнес-задача «сделать обратную нумерацию» на самом деле скрывает потребность пользователя видеть новые элементы вверху списка. Если причина именно в этом, то изменять логику нумерации — это избыточное и рискованное решение. Проанализируем, как добиться того же результата штатными средствами интерфейса.
В форме списка справочника мы можем настроить сортировку по умолчанию. Вместо того чтобы мучиться с убывающим кодом, мы оставляем стандартную нумерацию (1, 2, 3...), но в настройках динамического списка устанавливаем порядок по полю Ссылка (или ДатаСоздания, если такой реквизит есть) — по убыванию. Таким образом, пользователь всегда будет видеть последний созданный элемент первым в списке, при этом целостность стандартных механизмов 1С не будет нарушена.
Важный момент: Если вы все же решите использовать ручную нумерацию кодов, помните о префиксах информационных баз и организаций. Метод Число() выдаст ошибку, если в коде встретятся буквы (например, "БП-001"). В таких случаях необходимо программно отделять текстовый префикс от числовой части перед выполнением арифметических операций.
Подводя итог, можно сказать, что реализация убывающей нумерации в 1С возможна, но требует осторожности. Основные инструменты разработчика здесь — событие ПриУстановкеНовогоНомера, функции Формат для сохранения ведущих нулей и обязательное использование БлокировкаДанных для предотвращения дублей в многопользовательском режиме. Однако перед внедрением таких правок всегда стоит уточнить: не решит ли задачу простая настройка сортировки в форме списка?