При проектировании прикладных решений на платформе «1С:Предприятие» разработчики часто сталкиваются с задачей генерации уникальных человекочитаемых идентификаторов. Это могут быть номера договоров, артикулы товаров, номера дисконтных карт, штрихкоды или коды пакетов обмена. Использовать системный уникальный идентификатор (GUID) в таких случаях невозможно, так как пользователям необходим понятный буквенно-цифровой код (например, ОРГ-000045), состоящий из префикса и последовательно увеличивающегося счетчика.
В этой статье мы подробно проанализируем два основных архитектурных подхода к созданию собственного нумератора: использование стандартной автонумерации справочника и создание кастомного механизма на базе регистра сведений. Мы рассмотрим сильные и слабые стороны каждого варианта, а также разберем вопросы блокировок СУБД в высоконагруженных системах.
Самый простой и очевидный путь — переложить задачу генерации кодов на саму платформу 1С. Для этого создается отдельный служебный справочник (например, Справочник.НумераторОбъектов).
Рассмотрим по шагам, как настроить такой справочник:
Строка и определите необходимую длину (например, 10 или 15 символов).При таком подходе для получения нового номера достаточно программно создать элемент справочника, записать его и считать полученный код:
НовыйЭлемент = Справочники.НумераторОбъектов.СоздатьЭлемент();
НовыйЭлемент.УстановитьНовыйКод("ПРФ-"); // Установка префикса
НовыйЭлемент.Записать();
ПолученныйНомер = НовыйЭлемент.Код;
Преимущества использования справочника:
Недостатки и подводные камни:
Ссылка, ВерсияДанных, ПометкаУдаления, Предопределенный), что приводит к лишней нагрузке на индексы и росту объема базы при интенсивной генерации номеров.Второй популярный вариант — создание собственного счетчика в непериодическом регистре сведений. В этом случае мы храним текущее состояние счетчика в разрезе префиксов. Структура регистра (например, РегистрСведений.СобственныеНумераторы) выглядит следующим образом:
Префикс (тип Строка)ТекущееЗначение (тип Число)Для генерации нового номера нам нужно прочитать текущее значение счетчика по нужному префиксу, увеличить его на 1, записать новое значение обратно в регистр и сформировать строковое представление номера.
Проанализируем проблему конкурентного доступа. В многопользовательской среде два сеанса могут одновременно прочитать одно и то же максимальное значение из регистра. В результате оба прибавят единицу, получат одинаковые номера и попытаются записать их. Это приведет либо к дублированию номеров, либо к ошибке транзакции.
Чтобы избежать этой проблемы в управляемом режиме блокировок, необходимо использовать механизм управляемых блокировок. Мы должны принудительно заблокировать запись по конкретному префиксу перед чтением. Выстроив обращения пользователей в строгую очередь, мы гарантируем уникальность номеров.
Рассмотрим пример кода для получения номера через регистр сведений:
Функция ПолучитьСледующийНомер(ИмяПрефикса, ДлинаЧисла) Экспорт
НачатьТранзакцию();
Попытка
// 1. Установка управляемой блокировки на конкретный префикс
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.СобственныеНумераторы");
ЭлементБлокировки.УстановитьЗначение("Префикс", ИмяПрефикса);
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
Блокировка.Заблокировать();
// 2. Чтение текущего значения счетчика
Отбор = Новый Структура("Префикс", ИмяПрефикса);
ЗначениеСчетчика = 0;
Выборка = РегистрыСведений.СобственныеНумераторы.Выбрать(Отбор);
Если Выборка.Следующий() Тогда
ЗначениеСчетчика = Выборка.ТекущееЗначение;
КонецЕсли;
// Инкремент счетчика
НовоеЗначение = ЗначениеСчетчика + 1;
// 3. Запись обновленного значения в регистр
МенеджерЗаписи = РегистрыСведений.СобственныеНумераторы.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Префикс = ИмяПрефикса;
МенеджерЗаписи.ТекущееЗначение = НовоеЗначение;
МенеджерЗаписи.Записать(Истина);
ЗафиксироватьТранзакцию();
// 4. Форматирование номера (добавление префикса и лидирующих нулей)
ФорматированныйНомер = ФорматироватьНомер(ИмяПрефикса, НовоеЗначение, ДлинаЧисла);
Возврат ФорматированныйНомер;
Исключение
ОтменитьТранзакцию();
ВызватьИсключение "Не удалось сгенерировать номер: " + ОписаниеОшибки();
КонецПопытки;
КонецФункции
Функция ФорматироватьНомер(Префикс, ЧислоНомера, ОбщаяДлина)
СтрокаЧисла = Формат(ЧислоНомера, "ЧГ=0");
ДлинаПрефикса = СтрДлина(Префикс);
ДлинаНулей = ОбщаяДлина - ДлинаПрефикса - СтрДлина(СтрокаЧисла);
ЛидирующиеНули = "";
Для Счетчик = 1 По ДлинаНулей Цикл
ЛидирующиеНули = ЛидирующиеНули + "0";
КонецЦикла;
Возврат Префикс + ЛидирующиеНули + СтрокаЧисла;
КонецФункции
Преимущества использования регистра сведений:
Недостатки:
Выбор конкретного технического решения зависит от бизнес-требований к вашей системе: