При разработке расширений для ЗУП 3.1 или других современных конфигураций на базе БСП программисты часто сталкиваются с задачей: нужно создать дополнительный реквизит (например, НумОт_НомерТ60), который должен в точности повторять основной номер документа. Казалось бы, простая задача превращается в проблему, так как в привычных событиях объект еще не имеет номера или уже записан в базу, что делает его изменение невозможным без повторной записи.
В этой статье мы подробно разберем механику автонумерации 1С, выясним, почему стандартные подходы могут не работать, и рассмотрим правильные способы решения задачи.
Проанализируем ситуацию: разработчики платформы и типовых конфигураций давно отказались от стратегии присвоения номера в момент создания нового документа (при открытии формы). Рассмотрим основные причины такого решения:
В современных версиях 1С используется так называемая отложенная нумерация. Платформа старается выдать номер в самый последний момент — непосредственно во время транзакции записи. Именно поэтому в процедуре ПередЗаписью номер зачастую еще пуст.
Рассмотрим типичную ошибку начинающего разработчика, которая упоминалась в обсуждении. Попытка присвоить значение в процедуре ПриЗаписи выглядит логично, так как номер уже точно сформирован. Однако, как гласит справка 1С:
"Обработчик ПриЗаписи вызывается после записи объекта в базу данных, но до окончания транзакции записи."
Это означает, что данные уже "сброшены" в таблицы БД. Если вы напишете Объект.ДополнительныйРеквизит = Объект.Номер, значение изменится только в оперативной памяти (в текущем экземпляре объекта), но не в самой базе данных. Чтобы сохранить это изменение, пришлось бы вызывать метод Записать() повторно, что приведет к бесконечной рекурсии и аварийному завершению работы.
Один из вариантов решения — принудительно заставить платформу выдать номер перед тем, как объект будет зафиксирован в базе. Разберем пример кода, который можно внедрить через расширение в процедуру ПередЗаписью:
&После("ПередЗаписью")
Процедура НумОт_ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
// Проверяем, что это новый документ и номер еще не назначен
Если Не Отказ И ЭтоНовый() И ЗначениеЗаполнено(Номер) = Ложь Тогда
// Принудительно генерируем номер прямо сейчас
УстановитьНовыйНомер();
// Теперь мы можем скопировать его в наш реквизит
НумОт_НомерТ60 = Номер;
КонецЕсли;
КонецПроцедуры
Важный нюанс этого метода: Вызывая УстановитьНовыйНомер(), вы берете на себя ответственность за этот номер. Если после этого в коде возникнет ошибка или отказ от записи (параметр Отказ = Истина), номер все равно будет считаться "выданным" платформой, и в последовательности номеров может возникнуть пропуск.
Это наиболее "чистый" и правильный с точки зрения архитектуры способ. Событие ПриУстановкеНовогоНомера специально предназначено для вмешательства в процесс нумерации. Рассмотрим алгоритм его использования:
ПриУстановкеНовогоНомера.Пример реализации:
&После("ПриУстановкеНовогоНомера")
Процедура НумОт_ПриУстановкеНовогоНомера(СтандартнаяОбработка, Префикс)
// К этому моменту Номер уже содержит сгенерированное значение
НумОт_НомерТ60 = Номер;
КонецПроцедуры
Этот метод хорош тем, что он срабатывает только тогда, когда это действительно нужно (при первичной генерации номера) и интегрируется в стандартный жизненный цикл документа.
Проанализируем ситуацию, когда нам нужно узнать будущий номер документа еще до того, как мы нажали кнопку "Записать" (например, для вывода в печатную форму в режиме предпросмотра). Для этого можно использовать метод временного копирования объекта, как было предложено в одном из сообщений форума:
// Устанавливаем привилегированный режим для работы с нумерацией
УстановитьПривилегированныйРежим(Истина);
// Получаем объект из реквизитов формы
ДокументОбъект = РеквизитФормыВЗначение("Объект");
// Создаем временную копию
ВременныйОбъект = ДокументОбъект.Скопировать();
ВременныйОбъект.Дата = Объект.Дата;
// Генерируем номер для копии
ВременныйОбъект.УстановитьНовыйНомер();
// Будущий номер теперь известен
БудущийНомер = ВременныйОбъект.Номер;
Внимание: Будьте осторожны с этим методом в высоконагруженных системах, так как частое создание "временных" номеров может приводить к быстрому расходу диапазона нумерации, если эти номера не будут использованы в реальных документах.
Подведем итог нашего исследования механизмов работы с номерами в 1С:
ПриЗаписи для их сохранения в текущей транзакции — это бесполезно.ПриУстановкеНовогоНомера.ПередЗаписью, используйте явный вызов УстановитьНовыйНомер(), но помните о риске возникновения пропусков.Строка).Выяснив причину поведения платформы, вы сможете осознанно выбирать момент для модификации данных, не нарушая логику работы типовой конфигурации.