В процессе разработки конфигураций на платформе 1С:Предприятие 8.3 часто возникает задача хранения «чистого» номера документа — без лидирующих нулей и префиксов организации или информационной базы. Это удобно для интеграций с внешними системами, упрощения поиска в списках или вывода в специфические печатные формы. Однако начинающие разработчики часто сталкиваются с дилеммой: в какой момент заполнять этот реквизит, если основной Номер документа формируется платформой автоматически только в процессе записи?
Разберем детально, как решить эту задачу элегантно, сохранив производительность системы и целостность данных.
Проанализируем стандартный процесс записи нового документа. Когда мы нажимаем кнопку «Провести и закрыть» или программно вызываем метод Записать(), платформа инициирует транзакцию базы данных. События развиваются в следующем порядке:
Основная сложность заключается в том, что если мы попытаемся изменить значение реквизита в процедуре ПриЗаписи, нам может показаться, что нужно снова вызывать Записать(), чтобы изменения сохранились. Но вызов Записать() внутри обработчика ПриЗаписи — это классическая ошибка, ведущая к бесконечной рекурсии или как минимум к неоправданному увеличению нагрузки на сервер.
Выясним важную техническую деталь: обработчик ПриЗаписи в модуле объекта выполняется внутри той же самой транзакции, в которой записывается основной объект. Платформа 1С спроектирована таким образом, что любые изменения свойств объекта (реквизитов), выполненные в этой процедуре, будут включены в текущую транзакцию и зафиксированы в базе данных автоматически при ее завершении.
Рассмотрим пример кода, который нужно разместить в модуле объекта:
Процедура ПриЗаписи(Отказ)
// Проверяем, не выполняется ли загрузка данных (обмен)
Если ОбменДанными.Загрузка Тогда
Возврат;
КонецЕсли;
// Формируем красивое представление номера
// Допустим, функция ПрефиксацияОбъектовКлиентСервер.ПолучитьНомерНаПечать
// уже есть в вашей конфигурации (БСП)
НовоеПредставление = ПрефиксацияОбъектовКлиентСервер.ПолучитьНомерНаПечать(Номер, Истина, Истина);
// Если текущее значение в реквизите отличается от расчетного - обновляем
Если ПредставлениеНомера <> НовоеПредставление Тогда
ЭтотОбъект.ПредставлениеНомера = НовоеПредставление;
// Дополнительный вызов Записать() НЕ НУЖЕН!
КонецЕсли;
КонецПроцедуры
Важный нюанс: Несмотря на то что запись произойдет автоматически, такой подход считается «дозаписью» в рамках транзакции. С точки зрения архитектуры СУБД это вызывает дополнительный оператор UPDATE. Однако это гораздо «чище», чем повторный вызов полноценного метода Записать() со всеми проверками и подписками.
Если мы хотим, чтобы реквизит ПредставлениеНомера заполнился еще до того, как данные первый раз коснутся таблиц базы данных, проанализируем событие ПриУстановкеНовогоНомера. Это событие вызывается сразу после того, как платформа определила следующий свободный номер в последовательности.
Посмотрим на пример реализации в модуле объекта:
Процедура ПриУстановкеНовогоНомера(СтандартнаяОбработка, Префикс)
// Здесь мы можем вызвать стандартный механизм установки номера,
// чтобы он сформировался прямо сейчас
// Это произойдет автоматически, но мы можем переопределить логику
КонецПроцедуры
Однако работа с этим событием напрямую может быть избыточной. Более надежный способ — вызвать установку номера принудительно в ПередЗаписью:
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
Если ОбменДанными.Загрузка Тогда
Возврат;
КонецЕсли;
// Если это новый объект и номер еще не назначен
Если ЭтоНовый() И Не ЗначениеЗаполнено(Номер) Тогда
УстановитьНовыйНомер();
КонецЕсли;
// Теперь Номер гарантированно заполнен, и мы можем подготовить наше представление
ПредставлениеНомера = СделатьКрасивыйНомер(Номер);
КонецПроцедуры
Этот метод считается наиболее правильным с точки зрения производительности, так как данные записываются в БД один раз со всеми заполненными полями.
Проанализируем ситуацию: действительно ли нам нужно хранить этот номер в отдельном реквизите? Хранение дублирующих данных (избыточность) часто приводит к их десинхронизации. Например, если пользователь вручную отредактирует Номер, а логика обновления ПредставлениеНомера не сработает, данные «разъедутся».
Рассмотрим альтернативные варианты вывода «красивого» номера:
ПОДСТРОКА или математические операции (если номер числовой), чтобы убрать нули прямо при отображении.ПрефиксацияОбъектовКлиентСервер, содержащий функцию НомерНаПечать().Если вы все же решили создавать отдельный реквизит, обратите внимание на следующие моменты:
Индексирование: Если по реквизиту ПредставлениеНомера будет осуществляться поиск или фильтрация (например, из внешней системы через OData или в списках), обязательно установите свойство Индексировать (или Индексировать с доп. упорядочиванием). Без индекса поиск по строковому полю в большой таблице приведет к серьезным задержкам.
Подписки на события: Чтобы не вносить изменения в каждый документ (если их много), создайте общую Подписку на событие для типа ДокументОбъект на событие ПередЗаписью. Это позволит централизованно управлять логикой формирования представлений номеров для всей системы.
Функции БСП: Проанализируем возможности Библиотеки стандартных подсистем. Функция ПрефиксацияОбъектов.НомерНаПечать() умеет удалять не только лидирующие нули, но и префиксы организаций, префиксы информационных баз и пользовательские префиксы. Использование готовых механизмов сократит количество ошибок в вашем коде.
Подводя итог, можно сказать: наиболее «красивое» решение без лишних записей — это принудительный вызов УстановитьНовыйНомер() в процедуре ПередЗаписью с последующим заполнением вашего реквизита. Это гарантирует чистоту транзакции и актуальность данных.