Как реализовать условную иерархическую нумерацию документов в 1С:Предприятие

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

В практике автоматизации на платформе 1С часто возникает задача создания «связанной» нумерации. Типичный пример: основной документ реализации имеет номер 001, а все последующие уточняющие или дополнительные документы, созданные на его основании, должны получать номера вида 001/1, 001/2 и так далее. На первый взгляд задача кажется простой, но при работе большого количества пользователей (более 70 человек) стандартные механизмы могут приводить к ошибкам, блокировкам или сбою автонумерации. В этой статье мы подробно разберем, как правильно реализовать такой функционал, какие методы 1С использовать и как избежать типичных ловушек.

Использование стандартного обработчика события ПриУстановкеНовогоНомера

Основным инструментом для решения нашей задачи является обработчик события ПриУстановкеНовогоНомера, который располагается в модуле объекта документа. Этот обработчик вызывается системой в тот момент, когда документу необходимо присвоить номер (обычно при первом нажатии кнопки «Записать» или программном вызове метода УстановитьНовыйНомер()).

Проанализируем синтаксис этого метода. Он принимает два ключевых параметра: СтандартнаяОбработка (позволяет отключить системную генерацию) и Префикс. Рассмотрим базовый пример реализации, когда мы хотим подставить номер основного документа в качестве префикса:


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

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

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

Оптимизация передачи данных через ДополнительныеСвойства

Иногда при создании нового документа номер документа-основания может быть еще не доступен напрямую в базе или мы хотим избежать лишних обращений к базе данных внутри транзакции записи. В таких случаях профессиональным подходом считается использование коллекции ДополнительныеСвойства. Рассмотрим, как это работает на стороне формы и модуля объекта.

В форме документа при изменении реквизита ОсновнойДокумент мы можем подготовить данные:


&НаСервере
Процедура ПриИзмененииОсновногоДокументаНаСервере()
    Если ЗначениеЗаполнено(Объект.ОсновнойДокумент) Тогда
        // Кэшируем номер в дополнительные свойства, чтобы не считывать его повторно
        Объект.ДополнительныеСвойства.Вставить("НомерОснованияДляНумерации", Объект.ОсновнойДокумент.Номер);
    КонецЕсли;
КонецПроцедуры

Затем в модуле объекта мы используем эти данные для формирования номера. Проанализируем обновленный код:


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

Проблемы и риски: почему нумерация «сбивается»

Рассмотрим критически важный момент, упомянутый участниками форума: риск сбоя нумерации для «обычных» документов. Платформа 1С кэширует последние использованные номера для ускорения работы. Если вы запишете документ с номером 001/1, система может запомнить префикс 001/ как последний актуальный.

Основные причины проблем:

  1. Превышение длины номера: Строковый реквизит Номер имеет фиксированную длину (например, 11 символов). Если номер основного документа АБ000000001, то при добавлении /1 произойдет обрезка, и автонумератор не сможет корректно вычислить следующее число.
  2. Кэширование: При создании документа без основания система может ошибочно подставить префикс от предыдущего «дочернего» документа. Для борьбы с этим необходимо четко контролировать префиксацию для основных документов (например, всегда добавлять к ним ведущие нули или свой уникальный префикс).
  3. Блокировки (Deadlocks): При одновременной работе 70 пользователей система ищет «максимальный» номер в таблице. Если поиск выполняется по неиндексированным полям или внутри сложной транзакции, это приведет к очередям и ошибкам «Конфликт блокировок при выполнении транзакции».

Рекомендованная архитектура для высоконагруженных систем

Если проект предполагает большой объем данных, мы рекомендуем отказаться от хранения иерархического номера непосредственно в стандартном поле Номер. Разберем более надежный алгоритм:

  1. Создадим дополнительный реквизит ПредставлениеНомера (Строка).
  2. Стандартный Номер оставим для системы в обычном формате (00001, 00002).
  3. В событии ПередЗаписью будем формировать значение для ПредставлениеНомера.
  4. Для быстрого получения следующего индекса (1, 2, 3...) создадим Регистр Сведений НумерацияДочернихДокументов с измерениями ОсновнойДокумент и ресурсом ПоследнийНомер.

Проанализируем пример кода с использованием регистра сведений:


Функция ПолучитьСледующийПодномер(ДокументОснование)
    // Устанавливаем блокировку на запись в регистр для обеспечения уникальности
    Блокировка = Новый БлокировкаДанных;
    ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.НумерацияДочернихДокументов");
    ЭлементБлокировки.УстановитьЗначение("ОсновнойДокумент", ДокументОснование);
    Блокировка.Заблокировать();
    
    // Читаем текущее значение
    Менеджер = РегистрыСведений.НумерацияДочернихДокументов.СоздатьМенеджерЗаписи();
    Менеджер.ОсновнойДокумент = ДокументОснование;
    Менеджер.Прочитать();
    
    НовыйИндекс = Менеджер.ПоследнийНомер + 1;
    
    // Записываем обновленное значение
    Менеджер.ПоследнийНомер = НовыйИндекс;
    Менеджер.Записать();
    
    Возврат НовыйИндекс;
КонецФункции

Такой подход гарантирует отсутствие «дырок» в нумерации и исключает влияние на стандартный механизм автонумерации документов 1С. В списках документов и печатных формах пользователю следует выводить именно ПредставлениеНомера.

Законодательные нюансы (Счета-фактуры)

Важно помнить, что если ваш документ является основанием для Счет-фактуры в РФ, то использование произвольных дробей в номере может нарушать Постановление №1137. Налоговые органы допускают использование дроби в номерах счетов-фактур только в строго определенных случаях (например, продажа через обособленные подразделения). Перед внедрением подобной логики для регламентированного учета обязательно проконсультируйтесь с бухгалтером, так как нарушение хронологии номеров может стать причиной отказа в вычете НДС для ваших контрагентов.

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

← На главную