При разработке часто возникает задача разделить нумерацию элементов справочника или документов по определенному признаку, например, в зависимости от роли или подразделения текущего пользователя. Типичная реализация заключается в попытке назначить префикс только для выделенной группы пользователей, оставив для всех остальных стандартную нумерацию без префикса (пустую строку). Однако при таком подходе система начинает автоматически подставлять префикс даже тем элементам, для которых он явно не назначался. Разберем детально, почему это происходит и как правильно организовать разделение нумерации.
Рассмотрим пример кода в модуле объекта справочника, с которого обычно начинаются подобные сложности:
Процедура ПриУстановкеНовогоКода(СтандартнаяОбработка, Префикс)
Если РольДоступна("СпециальнаяГруппа") И ЭтоНовый() Тогда
Префикс = "~";
КонецЕсли;
КонецПроцедуры
На первый взгляд логика кажется верной: для пользователей с определенной ролью задается префикс ~, а для остальных переменная Префикс остается пустой строкой. Однако при создании нового контрагента обычным пользователем платформа все равно формирует код вида ~000000015. Проанализируем внутренние механизмы платформы «1С:Предприятие», чтобы понять причину такого поведения.
Выясним причину сбоя нумерации. Механизм автонумерации платформы функционирует по следующим правилам:
"~" или "ОСН-"), платформа ищет максимальный номер строго в рамках указанного префикса и увеличивает его числовую часть на единицу.""), платформа не ищет элементы исключительно с пустым префиксом. Вместо этого механизм автонумерации находит лексикографически максимальный префикс среди всех существующих записей в таблице базы данных (по правилам сортировки строковых значений СУБД) и берет его в качестве базового шаблона.Символ тильды ~ в таблице символов ASCII имеет код 126, что лексикографически старше цифр 0-9 (коды 48–57) и большинства букв. В результате при пустом префиксе СУБД возвращает код, начинающийся с ~, как максимальный, и платформа продолжает тиражировать этот префикс для всех последующих элементов.
Таким образом, в штатной автонумерации 1С не существует изолированной серии для «пустого префикса», если в этой же таблице присутствуют записи с непустыми префиксами. Рассмотрим основные пути решения этой проблемы.
Самый надежный и простой способ сохранить штатную автонумерацию без написания сложных алгоритмов блокировок — задать фиксированные префиксы для всех категорий пользователей, полностью исключив пустой префикс.
Посмотрим на пример реализации в событии ПриУстановкеНовогоКода:
Процедура ПриУстановкеНовогоКода(СтандартнаяОбработка, Префикс)
Если РольДоступна("СпециальнаяГруппа") Тогда
Префикс = "СПЕЦ-";
Иначе
Префикс = "ОСН-";
КонецЕсли;
КонецПроцедуры
В этом случае платформа видит две четко разделенные серии номеров: СПЕЦ-00001 и ОСН-00001. Нумерация по каждой серии будет вестись полностью независимо, без взаимного влияния и сбоев.
Если бизнес-требования категорически запрещают добавлять префикс для основных элементов справочника, штатную автонумерацию для них использовать не удастся. Потребуется отключить стандартную обработку и формировать код вручную.
Разберем по шагам, как реализовать собственный алгоритм нумерации:
СтандартнаяОбработка = Ложь;.Формат.Рассмотрим пример программной реализации:
Процедура ПриУстановкеНовогоКода(СтандартнаяОбработка, Префикс)
СтандартнаяОбработка = Ложь;
Если РольДоступна("СпециальнаяГруппа") Тогда
Префикс = "СП-";
Иначе
Префикс = "";
КонецЕсли;
Запрос = Новый Запрос;
Если ЗначениеЗаполнено(Префикс) Тогда
Запрос.Текст =
"ВЫБРАТЬ
| МАКСИМУМ(Контрагенты.Код) КАК МаксКод
|ИЗ
| Справочник.Контрагенты КАК Контрагенты
|ГДЕ
| Контрагенты.Код ПОДОБНО &ШаблонПрефикса";
Запрос.УстановитьПараметр("ШаблонПрефикса", Префикс + "%");
Иначе
Запрос.Текст =
"ВЫБРАТЬ
| МАКСИМУМ(Контрагенты.Код) КАК МаксКод
|ИЗ
| Справочник.Контрагенты КАК Контрагенты
|ГДЕ
| Контрагенты.Код ПОДОБНО '[0-9]%'";
КонецЕсли;
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
НовыйЧисловойКод = 1;
Если Выборка.Следующий() И ЗначениеЗаполнено(Выборка.МаксКод) Тогда
МаксимальныйКод = СокрЛП(Выборка.МаксКод);
Если ЗначениеЗаполнено(Префикс) Тогда
МаксимальныйКод = Сред(МаксимальныйКод, СтрДлина(Префикс) + 1);
КонецЕсли;
Попытка
НовыйЧисловойКод = Число(МаксимальныйКод) + 1;
Исключение
НовыйЧисловойКод = 1;
КонецПопытки;
КонецЕсли;
ДлинаКода = Метаданные().ДлинаКода;
ДлинаЧисловойЧасти = ДлинаКода - СтрДлина(Префикс);
ФорматЧисла = "ЧЦ=" + Формат(ДлинаЧисловойЧасти, "ЧГ=0") + "; ЧВН=; ЧГ=0";
ЭтотОбъект.Код = Префикс + Формат(НовыйЧисловойКод, ФорматЧисла);
КонецПроцедуры
Важно: При реализации собственной нумерации в высоконагруженных многопользовательских базах генерацию кода необходимо выполнять в транзакции перед записью с обязательной установкой управляемой блокировки на пространство кодов справочника, чтобы исключить возникновение дублей.
Если разработка ведется в типовой конфигурации на базе «Библиотеки стандартных подсистем» (БСП), не рекомендуется переопределять модуль объекта напрямую. Вместо этого следует использовать встроенные механизмы подсистемы префиксации.
Для добавления пользовательской логики префиксов используется процедура ПриУстановкеНовогоКода общего модуля ПрефиксацияОбъектовПереопределяемый. Это позволяет сохранить совместимость с механизмами нумерации БСП, корректно обрабатывать префиксы узлов РИБ и префиксы организаций, а также гарантирует корректное обновление конфигурации в будущем.