При автоматизации документооборота в конфигурации 1С:Управление торговлей 11 часто возникает бизнес-требование: формировать независимую последовательную нумерацию документов для каждого подразделения или филиала организации. Например, пользователи подразделения хотят видеть номера вида «17КА-000001», а сотрудники другого филиала — «17КО-000001», при этом документы не должны сбивать порядковый счетчик друг друга.
Попытка реализовать такую задачу «в лоб» через подписки на события ПриЗаписи или прямое присвоение номера вручную часто приводит к ошибкам: нарушению контроля уникальности, неконтролируемым скачкам номеров и взаимным блокировкам сеансов. Разберем по шагам, почему возникают сбои и как правильно настроить параллельную нумерацию.
Проанализируем частые ошибки разработчиков, пытающихся написать собственный генератор номеров в коде подписок:
Записать() внутри обработчиков событий. Категорически нельзя вызывать Источник.Записать() внутри обработчиков ПередЗаписью, ПриЗаписи или ПриУстановкеНовогоНомера. Это инициирует рекурсивный запуск транзакции записи, приводит к блокировкам базы данных и повторным проверкам уникальности на незафиксированных данных.17КА-1), то при достижении значения 17КА-9 платформа не сможет корректно выделить разряд, что приведет либо к повторному присвоению девятки, либо к нарушению уникальности. Кроме того, лексикографически строка 17КА-10 меньше, чем 17КА-9, что полностью ломает автопоиск максимума платформой.ПриЗаписи для нумерации. К моменту наступления события ПриЗаписи платформа уже зафиксировала номер объекта и выполнила проверку на уникальность. Любые манипуляции с реквизитом Номер должны происходить строго до записи или в специализированных событиях нумерации.В конфигурациях на базе Библиотеки стандартных подсистем (включая «1С:Управление торговлей 11») нумерацией управляет штатная подсистема префиксации. Платформа 1С поддерживает раздельные пространства нумерации для номеров с разными префиксами. Если задать каждому подразделению собственный префикс, платформа самостоятельно будет вести независимый счетчик для каждого из них с соблюдением всех правил блокировок и лидирующих нулей.
Разберем, как подключить префикс подразделения к документу:
ПрефиксацияОбъектовПереопределяемый.ПриУстановкеНовогоНомераДокумента и добавим логику назначения префикса.Посмотрим на пример реализации в процедуре переопределяемого модуля:
Процедура ПриУстановкеНовогоНомераДокумента(Источник, Префикс) Экспорт
Если ТипЗнч(Источник) = Тип("ДокументОбъект.Замер") Тогда
// Определяем префикс на основании данных подразделения
Если ЗначениеЗаполнено(Источник.Подразделение) Тогда
ПрефиксПодразделения = СокрЛП(Источник.Подразделение.Префикс);
Если Не ПустаяСтрока(ПрефиксПодразделения) Тогда
Префикс = ПрефиксПодразделения + Префикс;
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
При таком подходе платформа и механизмы БСП сформируют корректный номер с лидирующими нулями (например, КА-000000001 и КО-000000001). Каждое подразделение получит независимую непрерывную цепочку документов, а контроль уникальности отработает штатно.
Если в процессе разработки и отладки номеров в базу уже были записаны некорректные номера (с нарушенной разрядностью или пропущенными нулями), внутренний кэш сервера 1С может запомнить некорректный последний номер. В такой ситуации даже правильный алгоритм может продолжать выдавать ошибки дублирования.
Для исправления ситуации выполним принудительное обновление пространства нумерации через вызов встроенной функции платформы во внешней обработке:
ОбновитьНумерациюОбъектов(Метаданные.Документы.Замер);
Этот метод сбрасывает закэшированные диапазоны номеров в оперативной памяти сервера 1С и заставляет систему заново пересчитать максимальный занятый номер непосредственно по таблице базы данных.
Рассмотрим альтернативную ситуацию: когда бизнес-требования диктуют жесткий и нестандартный формат номера, который невозможно уложить в типовую структуру префиксов БСП (например, 17-КА/01).
В этом случае использовать запросы по всей таблице документов крайне не рекомендуется из-за риска взаимоблокировок. Разберем правильную схему на основе вспомогательного регистра:
СчетчикиНумерации.Подразделение (СправочникСсылка.СтруктураПредприятия) и Год (Число), а также ресурс ТекущийНомер (Число).ПередЗаписью получаем следующий номер в транзакции с явной установкой исключительной управляемой блокировки данных.Посмотрим на пример кода в модуле объекта документа:
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
Если Отказ Или Не ЭтоНовый() Тогда
Возврат;
КонецЕсли;
// Блокируем запись регистра для исключения race condition
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.СчетчикиНумерации");
ЭлементБлокировки.УстановитьЗначение("Подразделение", ЭтотОбъект.Подразделение);
ЭлементБлокировки.УстановитьЗначение("Год", Год(ЭтотОбъект.Дата));
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
Блокировка.Заблокировать();
// Считываем текущий номер
НаборЗаписей = РегистрыСведений.СчетчикиНумерации.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Подразделение.Установить(ЭтотОбъект.Подразделение);
НаборЗаписей.Отбор.Год.Установить(Год(ЭтотОбъект.Дата));
НаборЗаписей.Прочитать();
НовыйСчетчик = 1;
Если НаборЗаписей.Количество() > 0 Тогда
НовыйСчетчик = НаборЗаписей[0].ТекущийНомер + 1;
НаборЗаписей[0].ТекущийНомер = НовыйСчетчик;
Иначе
Запись = НаборЗаписей.Добавить();
Запись.Подразделение = ЭтотОбъект.Подразделение;
Запись.Год = Год(ЭтотОбъект.Дата);
Запись.ТекущийНомер = НовыйСчетчик;
КонецЕсли;
НаборЗаписей.Записать();
// Форматируем числовую часть с обязательным сохранением ведущих нулей
СтрокаЧисла = Формат(НовыйСчетчик, "ЧЦ=6; ЧВН=; ЧГ=");
ЭтотОбъект.Номер = Строка(Год(ЭтотОбъект.Дата)) + ЭтотОбъект.Подразделение.Префикс + "-" + СтрокаЧисла;
КонецПроцедуры
Данная схема исключает тяжелые запросы по всей таблице документов при записи, предотвращает коллизии параллельных сеансов и гарантирует непрерывную и раздельную нумерацию документов в системе.