Как реализовать раздельную нумерацию документов по подразделениям в 1С:УТ 11?

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет IT и автоматизация бизнеса
← На главную

При автоматизации документооборота в конфигурации 1С:Управление торговлей 11 часто возникает бизнес-требование: формировать независимую последовательную нумерацию документов для каждого подразделения или филиала организации. Например, пользователи подразделения хотят видеть номера вида «17КА-000001», а сотрудники другого филиала — «17КО-000001», при этом документы не должны сбивать порядковый счетчик друг друга.

Попытка реализовать такую задачу «в лоб» через подписки на события ПриЗаписи или прямое присвоение номера вручную часто приводит к ошибкам: нарушению контроля уникальности, неконтролируемым скачкам номеров и взаимным блокировкам сеансов. Разберем по шагам, почему возникают сбои и как правильно настроить параллельную нумерацию.

Анализ типичных ошибок при ручном формировании номера

Проанализируем частые ошибки разработчиков, пытающихся написать собственный генератор номеров в коде подписок:

  1. Вызов метода Записать() внутри обработчиков событий. Категорически нельзя вызывать Источник.Записать() внутри обработчиков ПередЗаписью, ПриЗаписи или ПриУстановкеНовогоНомера. Это инициирует рекурсивный запуск транзакции записи, приводит к блокировкам базы данных и повторным проверкам уникальности на незафиксированных данных.
  2. Отсутствие лидирующих нулей в строковом номере. Платформа 1С выполняет инкремент строковых номеров справа налево с сохранением исходной длины числовой части. Если номер формируется без ведущих нулей (например, 17КА-1), то при достижении значения 17КА-9 платформа не сможет корректно выделить разряд, что приведет либо к повторному присвоению девятки, либо к нарушению уникальности. Кроме того, лексикографически строка 17КА-10 меньше, чем 17КА-9, что полностью ломает автопоиск максимума платформой.
  3. Гонка данных (Race Condition) при поиске максимума запросом. Если вычислять максимальный номер с помощью запроса без наложения исключительной управляемой блокировки, два пользователя при одновременной записи документов получат одинаковый номер. В результате у одного из них документ запишется, а второй получит ошибку контроля уникальности.
  4. Использование обработчика ПриЗаписи для нумерации. К моменту наступления события ПриЗаписи платформа уже зафиксировала номер объекта и выполнила проверку на уникальность. Любые манипуляции с реквизитом Номер должны происходить строго до записи или в специализированных событиях нумерации.

Решение 1. Штатный механизм БСП через префиксацию

В конфигурациях на базе Библиотеки стандартных подсистем (включая «1С:Управление торговлей 11») нумерацией управляет штатная подсистема префиксации. Платформа 1С поддерживает раздельные пространства нумерации для номеров с разными префиксами. Если задать каждому подразделению собственный префикс, платформа самостоятельно будет вести независимый счетчик для каждого из них с соблюдением всех правил блокировок и лидирующих нулей.

Разберем, как подключить префикс подразделения к документу:

  1. Проверим периодичность документа в дереве метаданных. Если установить свойство Периодичность в значение В пределах года, платформа будет автоматически начинать нумерацию заново каждый календарный год без необходимости вручную конкатенировать год в строке номера.
  2. Перейдем в общий модуль ПрефиксацияОбъектовПереопределяемый.
  3. Найдем экспортную процедуру ПриУстановкеНовогоНомераДокумента и добавим логику назначения префикса.

Посмотрим на пример реализации в процедуре переопределяемого модуля:


Процедура ПриУстановкеНовогоНомераДокумента(Источник, Префикс) Экспорт

    Если ТипЗнч(Источник) = Тип("ДокументОбъект.Замер") Тогда
        // Определяем префикс на основании данных подразделения
        Если ЗначениеЗаполнено(Источник.Подразделение) Тогда
            ПрефиксПодразделения = СокрЛП(Источник.Подразделение.Префикс);
            Если Не ПустаяСтрока(ПрефиксПодразделения) Тогда
                Префикс = ПрефиксПодразделения + Префикс;
            КонецЕсли;
        КонецЕсли;
    КонецЕсли;

КонецПроцедуры

При таком подходе платформа и механизмы БСП сформируют корректный номер с лидирующими нулями (например, КА-000000001 и КО-000000001). Каждое подразделение получит независимую непрерывную цепочку документов, а контроль уникальности отработает штатно.

Решение 2. Сброс кэша нумератора платформы при сбоях

Если в процессе разработки и отладки номеров в базу уже были записаны некорректные номера (с нарушенной разрядностью или пропущенными нулями), внутренний кэш сервера 1С может запомнить некорректный последний номер. В такой ситуации даже правильный алгоритм может продолжать выдавать ошибки дублирования.

Для исправления ситуации выполним принудительное обновление пространства нумерации через вызов встроенной функции платформы во внешней обработке:

ОбновитьНумерациюОбъектов(Метаданные.Документы.Замер);

Этот метод сбрасывает закэшированные диапазоны номеров в оперативной памяти сервера 1С и заставляет систему заново пересчитать максимальный занятый номер непосредственно по таблице базы данных.

Решение 3. Организация собственного счетчика через независимый регистр сведений

Рассмотрим альтернативную ситуацию: когда бизнес-требования диктуют жесткий и нестандартный формат номера, который невозможно уложить в типовую структуру префиксов БСП (например, 17-КА/01).

В этом случае использовать запросы по всей таблице документов крайне не рекомендуется из-за риска взаимоблокировок. Разберем правильную схему на основе вспомогательного регистра:

  1. Создаем непериодический регистр сведений СчетчикиНумерации.
  2. В регистре создаем измерения Подразделение (СправочникСсылка.СтруктураПредприятия) и Год (Число), а также ресурс ТекущийНомер (Число).
  3. В модуле документа в обработчике ПередЗаписью получаем следующий номер в транзакции с явной установкой исключительной управляемой блокировки данных.

Посмотрим на пример кода в модуле объекта документа:


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

КонецПроцедуры

Данная схема исключает тяжелые запросы по всей таблице документов при записи, предотвращает коллизии параллельных сеансов и гарантирует непрерывную и раздельную нумерацию документов в системе.

← На главную