В практике автоматизации на платформе 1С часто возникает задача создания «связанной» нумерации. Типичный пример: основной документ реализации имеет номер 001, а все последующие уточняющие или дополнительные документы, созданные на его основании, должны получать номера вида 001/1, 001/2 и так далее. На первый взгляд задача кажется простой, но при работе большого количества пользователей (более 70 человек) стандартные механизмы могут приводить к ошибкам, блокировкам или сбою автонумерации. В этой статье мы подробно разберем, как правильно реализовать такой функционал, какие методы 1С использовать и как избежать типичных ловушек.
Основным инструментом для решения нашей задачи является обработчик события ПриУстановкеНовогоНомера, который располагается в модуле объекта документа. Этот обработчик вызывается системой в тот момент, когда документу необходимо присвоить номер (обычно при первом нажатии кнопки «Записать» или программном вызове метода УстановитьНовыйНомер()).
Проанализируем синтаксис этого метода. Он принимает два ключевых параметра: СтандартнаяОбработка (позволяет отключить системную генерацию) и Префикс. Рассмотрим базовый пример реализации, когда мы хотим подставить номер основного документа в качестве префикса:
Процедура ПриУстановкеНовогоНомера(СтандартнаяОбработка, Префикс)
// Проверяем, заполнено ли поле ОсновнойДокумент
Если ЗначениеЗаполнено(ЭтотОбъект.ОсновнойДокумент) Тогда
// Получаем номер основного документа
НомерОснования = СокрЛП(ЭтотОбъект.ОсновнойДокумент.Номер);
// Устанавливаем префикс как номер основания плюс разделитель
// Стандартный нумератор добавит порядковый номер после дроби автоматически
Префикс = НомерОснования + "/";
КонецЕсли;
КонецПроцедуры
В данном сценарии мы полагаемся на то, что платформа 1С увидит в префиксе строку 001/ и попытается найти в базе данных документы, номера которых начинаются с этой строки, чтобы инкрементировать (увеличить на единицу) числовую часть, идущую после разделителя.
Иногда при создании нового документа номер документа-основания может быть еще не доступен напрямую в базе или мы хотим избежать лишних обращений к базе данных внутри транзакции записи. В таких случаях профессиональным подходом считается использование коллекции ДополнительныеСвойства. Рассмотрим, как это работает на стороне формы и модуля объекта.
В форме документа при изменении реквизита ОсновнойДокумент мы можем подготовить данные:
&НаСервере
Процедура ПриИзмененииОсновногоДокументаНаСервере()
Если ЗначениеЗаполнено(Объект.ОсновнойДокумент) Тогда
// Кэшируем номер в дополнительные свойства, чтобы не считывать его повторно
Объект.ДополнительныеСвойства.Вставить("НомерОснованияДляНумерации", Объект.ОсновнойДокумент.Номер);
КонецЕсли;
КонецПроцедуры
Затем в модуле объекта мы используем эти данные для формирования номера. Проанализируем обновленный код:
Процедура ПриУстановкеНовогоНомера(СтандартнаяОбработка, Префикс)
Если ЭтотОбъект.ДополнительныеСвойства.Свойство("НомерОснованияДляНумерации") Тогда
Префикс = СокрЛП(ЭтотОбъект.ДополнительныеСвойства.НомерОснованияДляНумерации) + "/";
ИначеЕсли ЗначениеЗаполнено(ЭтотОбъект.ОсновнойДокумент) Тогда
Префикс = СокрЛП(ЭтотОбъект.ОсновнойДокумент.Номер) + "/";
КонецЕсли;
КонецПроцедуры
Рассмотрим критически важный момент, упомянутый участниками форума: риск сбоя нумерации для «обычных» документов. Платформа 1С кэширует последние использованные номера для ускорения работы. Если вы запишете документ с номером 001/1, система может запомнить префикс 001/ как последний актуальный.
Основные причины проблем:
Номер имеет фиксированную длину (например, 11 символов). Если номер основного документа АБ000000001, то при добавлении /1 произойдет обрезка, и автонумератор не сможет корректно вычислить следующее число.Если проект предполагает большой объем данных, мы рекомендуем отказаться от хранения иерархического номера непосредственно в стандартном поле Номер. Разберем более надежный алгоритм:
ПредставлениеНомера (Строка).Номер оставим для системы в обычном формате (00001, 00002).ПередЗаписью будем формировать значение для ПредставлениеНомера.НумерацияДочернихДокументов с измерениями ОсновнойДокумент и ресурсом ПоследнийНомер.Проанализируем пример кода с использованием регистра сведений:
Функция ПолучитьСледующийПодномер(ДокументОснование)
// Устанавливаем блокировку на запись в регистр для обеспечения уникальности
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.НумерацияДочернихДокументов");
ЭлементБлокировки.УстановитьЗначение("ОсновнойДокумент", ДокументОснование);
Блокировка.Заблокировать();
// Читаем текущее значение
Менеджер = РегистрыСведений.НумерацияДочернихДокументов.СоздатьМенеджерЗаписи();
Менеджер.ОсновнойДокумент = ДокументОснование;
Менеджер.Прочитать();
НовыйИндекс = Менеджер.ПоследнийНомер + 1;
// Записываем обновленное значение
Менеджер.ПоследнийНомер = НовыйИндекс;
Менеджер.Записать();
Возврат НовыйИндекс;
КонецФункции
Такой подход гарантирует отсутствие «дырок» в нумерации и исключает влияние на стандартный механизм автонумерации документов 1С. В списках документов и печатных формах пользователю следует выводить именно ПредставлениеНомера.
Важно помнить, что если ваш документ является основанием для Счет-фактуры в РФ, то использование произвольных дробей в номере может нарушать Постановление №1137. Налоговые органы допускают использование дроби в номерах счетов-фактур только в строго определенных случаях (например, продажа через обособленные подразделения). Перед внедрением подобной логики для регламентированного учета обязательно проконсультируйтесь с бухгалтером, так как нарушение хронологии номеров может стать причиной отказа в вычете НДС для ваших контрагентов.
Подведем итог: для простых задач достаточно использования ПриУстановкеНовогоНомера с контролем префикса, но для стабильной работы крупной базы лучше использовать разделение системного номера и пользовательского представления через дополнительные реквизиты и регистры сведений.