Как правильно реализовать автоматическую нумерацию документов по префиксу заказчика в 1С 8.3?

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

В процессе разработки конфигураций на платформе 1С:Предприятие 8 часто возникает задача реализации индивидуальной нумерации документов для различных контрагентов или заказчиков. Например, при выборе заказчика в документе его номер должен автоматически принимать вид ПРЕФИКС/00001, где префикс жестко привязан к конкретному контрагенту. Попытки решить эту задачу «в лоб» через ручной перебор существующих номеров в базе данных приводят к серьезным ошибкам, зависаниям системы и нарушению уникальности номеров. В этой статье мы подробно проанализируем ошибки ручного подхода и разберем правильные, производительные методы решения этой задачи с использованием штатных механизмов платформы 1С.

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

Рассмотрим код, который часто пытаются использовать начинающие разработчики (или специалисты, перешедшие с устаревшей платформы 1С 7.7). Алгоритм пытается в цикле искать существующие документы и прибавлять единицу к номеру:


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

Пока 1 = 1 Цикл
    Если Документы.ЗаявкаНаПеревозку.НайтиПоНомеру(Префикс + Ном, Док.Дата) = 1 Тогда
        Если Объект.Ссылка = Док.Ссылка Тогда
            Док.Номер = Префикс + Ном;
            Прервать;
        КонецЕсли;
        Ном = Ном + 1;
        Продолжить;
    Иначе
        Объект.Номер = Префикс + Ном;
        Прервать;
    КонецЕсли;
КонецЦикла;

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

  1. Некорректное сравнение результата поиска. Метод НайтиПоНомеру возвращает объект типа ДокументСсылка (если документ найден) или значение Неопределено (если документ не найден). Сравнение полученного результата с числом 1 (как в конструкции ... = 1 Тогда) всегда будет ложным. В итоге код всегда будет уходить в ветку Иначе и присваивать первому же документу номер с окончанием 1, игнорируя существующие документы в базе.
  2. Сравнение ссылок у новых (незаписанных) документов. Конструкция Объект.Ссылка = Док.Ссылка не имеет смысла для новых документов. Метод СоздатьДокумент() создает в оперативной памяти новый объект, ссылка которого в базе данных еще не сформирована (она пустая). Ссылка у нового документа Объект, который еще не записан, также является пустой. Сравнение двух пустых ссылок вернет Истина, но логика работы программы при этом полностью нарушается.
  3. Проблема конкурентного доступа (блокировки). Представим ситуацию, когда два пользователя одновременно создают документ по одному и тому же заказчику. Их транзакции запустят этот цикл параллельно. Обе транзакции найдут один и тот же свободный номер (например, ЗАКАЗ/00025). При попытке записи документа первый пользователь успешно сохранит его, а второй получит критическую системную ошибку нарушения уникальности номера.
  4. Катастрофическое падение производительности (проблема N+1 запросов). Метод НайтиПоНомеру выполняет полноценный SQL-запрос к базе данных. Если по данному заказчику в базе уже создано 10 000 документов, то при создании 10 001-го документа ваш цикл выполнит ровно 10 000 последовательных запросов к СУБД! Это приведет к моментальной перегрузке сервера и полному зависанию сеанса пользователя.

Для решения этой задачи в платформе 1С:Предприятие 8 реализован мощный встроенный механизм автонумерации. Нам не нужно самостоятельно искать свободные номера в цикле — платформа умеет делать это автоматически и транзакционно, предотвращая конкурентные конфликты. Рассмотрим правильные способы реализации.

Решение №1: Использование обработчика ПриУстановкеНовогоНомера (Рекомендуемый подход)

Самым надежным и архитектурно правильным способом является назначение префикса в модуле объекта документа. Это гарантирует непрерывность нумерации и корректную работу транзакций в многопользовательской среде. Процесс генерации номера происходит непосредственно в момент записи документа в базу данных.

Откроем Модуль объекта нашего документа (например, ЗаявкаНаПеревозку) и найдем предопределенную процедуру-обработчик ПриУстановкеНовогоНомера. Напишем в ней следующий код:


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

При таком подходе, когда пользователь нажимает кнопку «Записать» или «Провести», платформа перехватывает управление, видит измененный параметр Префикс, самостоятельно находит максимальный номер с этим префиксом в базе данных и инкрементирует его числовую часть (например, превращая ИВАНОВ/00012 в ИВАНОВ/00013) на уровне СУБД с наложением кратковременной блокировки.

Решение №2: Принудительное назначение номера на форме (при изменении реквизита)

Если бизнес-требования вашей задачи жестко диктуют, что пользователь должен видеть итоговый номер документа сразу же на форме (в момент выбора заказчика, до записи документа в базу), мы можем использовать метод объекта УстановитьНовыйНомер().

Для этого в форме документа для поля ввода Заказчик настроим событие ПриИзменении. Разберем по шагам реализацию этого механизма:


&НаКлиенте
Процедура ЗаказчикПриИзменении(Элемент)
    
    // Передаем управление на сервер для работы с объектом документа
    УстановитьНомерПоЗаказчикуНаСервере();
    
КонецПроцедуры

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

Метод УстановитьНовыйНомер() мгновенно анализирует базу данных, генерирует следующий уникальный номер с переданным префиксом и записывает его в реквизит Номер нашего объекта. Пользователь сразу увидит актуальный номер на экране.

Важные технические нюансы, о которых нельзя забывать

При работе с префиксацией и автонумерацией документов в 1С необходимо учитывать несколько критических факторов:

  1. Длина реквизита «Номер» в метаданных. По умолчанию в конфигураторе длина номера документа часто ограничена (например, 9 или 11 символами). Если код нумерации вашего заказчика длинный (например, ТрансЛогистик — 13 символов), то префикс ТрансЛогистик/ физически не поместится в поле номера, либо сотрет его цифровую часть. Обязательно зайдите в свойства вашего документа в Конфигураторе и увеличьте длину номера с запасом (например, до 20-25 символов).
  2. Длина префиксов должна быть одинаковой для одного заказчика. Автонумератор платформы ориентируется на текстовое соответствие префикса. Если у вас для одного заказчика сначала запишется номер ТЛ/001, а затем вы измените логику и префикс станет ТЛ/0001, платформа может начать сбиваться при определении следующего свободного номера.
  3. Очистка лидирующих нулей. Платформа 1С автоматически дополняет оставшуюся часть номера нулями (например, А/0000001). Не пытайтесь вручную убирать эти нули или писать сложные парсеры строк. Платформа делает это для сохранения правильной сортировки номеров на уровне СУБД. Нарушение этой структуры приведет к тому, что после номера 9 новым номером станет не 10, а снова 9 из-за особенностей текстовой сортировки.
  4. Интеграция с Библиотекой Стандартных Подсистем (БСП). Если вы работаете в типовой конфигурации (например, 1С:ERP, Управление торговлей или Бухгалтерия предприятия), в них за автонумерацию отвечает специальный механизм БСП. Напрямую переопределять префикс в событии ПриУстановкеНовогоНомера может быть неэффективно, так как БСП затрет ваши изменения префиксами организации или узла распределенной базы данных (РИБ). В таких конфигурациях правильнее использовать общий модуль ПрефиксацияОбъектовСобытия и добавлять свои алгоритмы через подписки на события или механизмы интеграции БСП.

Таким образом, мы выяснили причину неработоспособности ручного кода, проанализировали опасности его использования и внедрили два надежных решения на базе штатного автонумератора 1С. Использование обработчика ПриУстановкеНовогоНомера или метода УстановитьНовыйНомер() гарантирует быструю, стабильную и безопасную работу вашей базы данных даже при одновременной работе сотен пользователей.

← На главную