Как записать номер документа в дополнительный реквизит без появления модифицированности формы

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

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

Почему возникает бесконечная модифицированность?

Проанализируем механику работы платформы. Когда мы нажимаем кнопку «Записать», данные формы уходят на сервер, превращаются в объект, записываются в базу данных и возвращаются обратно на форму. Если мы пытаемся изменить реквизит документа в событии ПриЗаписи или (что еще хуже) после записи на сервере, состояние объекта в базе данных начинает отличаться от состояния данных в расширении формы на клиенте.

Платформа видит, что данные в объекте на сервере изменились уже после того, как основной цикл записи завершился, и помечает форму как модифицированную. Если пользователь снова нажмет «Записать», код сработает еще раз, изменит данные, и форма снова станет модифицированной. Так возникает бесконечный цикл. Наша задача — поместить код в ту точку события, где номер уже известен, но запись еще не завершена окончательно.

Решение 1: Использование метода УстановитьНовыйНомер в событии ПередЗаписью

Самый простой способ, предложенный в обсуждении, — это принудительная генерация номера до того, как платформа сделает это автоматически. В обычном режиме номер присваивается документу в самый последний момент перед фиксацией транзакции. Мы можем ускорить этот процесс.

Разберем реализацию в модуле объекта документа:


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

Преимущества этого метода: реквизит заполняется еще до того, как данные попадут в базу, поэтому форма не считает себя «измененной» после записи.

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

Решение 2: Обработчик ПриУстановкеНовогоНомера (наиболее правильный)

Рассмотрим более элегантный архитектурный подход. В платформе 1С существует специализированное событие ПриУстановкеНовогоНомера. Оно срабатывает ровно в тот момент, когда система генерирует новый номер для объекта. Это идеальное место для синхронизации номера с вашим реквизитом.

Пример кода в модуле объекта:


Процедура ПриУстановкеНовогоНомера(СтандартнаяОбработка, Префикс)
    
    // Позволяем платформе самой сгенерировать номер, 
    // но сразу после этого можем использовать его
    // Внимание: на момент выполнения этой процедуры Номер еще может быть не заполнен,
    // если мы не вызовем генерацию явно.
    
КонецПроцедуры

Однако здесь есть тонкость: в самой процедуре ПриУстановкеНовогоНомера номер еще только формируется. Поэтому наиболее надежным остается комбинированный метод в ПередЗаписью, либо использование события ПриЗаписи с особым флагом.

Решение 3: Использование ДополнительныхСвойств для обмена данными

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

Выясним причину, по которой это удобно: вы можете установить флаг на форме, а в модуле объекта в событии ПриЗаписи (когда номер уже 100% есть) выполнить запись напрямую в базу через запрос или через создание вспомогательного объекта, чтобы не трогать текущий экземпляр ЭтотОбъект.

Но чаще всего достаточно простого обновления на форме. Чтобы форма «узнала» об изменении реквизита на сервере без перезагрузки, в модуле формы можно использовать метод:


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

Решение 4: Подписка на событие (для типовых конфигураций)

Если вам нужно внедрить эту логику в типовую конфигурацию (например, 1С:ERP или БП 3.0), и вы не хотите снимать документ с поддержки, лучше всего использовать Подписку на событие.

  1. Создайте новый общий модуль (серверный).
  2. Создайте объект «Подписка на событие».
  3. Источник: ваш тип документа.
  4. Событие: ПередЗаписью.

Код в общем модуле будет выглядеть так:


Процедура СинхронизироватьНомерПередЗаписью(Источник, Отказ, РежимЗаписи, РежимПроведения) Экспорт
    
    // Источник — это сам документ
    Если Источник.ЭтоНовый() И ПустаяСтрока(Источник.Номер) Тогда
        Источник.УстановитьНовыйНомер();
    КонецЕсли;
    
    Источник.ДополнительныйРеквизитНомер = Источник.Номер;
    
КонецПроцедуры

Проблема перенумерации в будущем

Проанализируем ситуацию: что произойдет, если бухгалтер вручную изменит номер документа через месяц или воспользуется групповой обработкой перенумерации?

В этом случае значение в вашем дополнительном реквизите станет неактуальным (там останется старый номер). Чтобы данные всегда были синхронны, логику записи номера нужно оставлять в ПередЗаписью всегда, а не только для новых документов.

Посмотрим на финальный, самый надежный вариант кода для модуля объекта:


Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    
    // Если это обмен данными, пропускаем логику
    Если ОбменДанными.Загрузка Тогда
        Возврат;
    КонецЕсли;
    
    // Если номер еще не присвоен (новый документ), присваиваем его
    Если ПустаяСтрока(ЭтотОбъект.Номер) Тогда
        ЭтотОбъект.УстановитьНовыйНомер();
    КонецЕсли;
    
    // Всегда синхронизируем реквизит с номером, 
    // чтобы при ручном изменении номера реквизит тоже обновился
    Если ЭтотОбъект.ДополнительныйРеквизитНомер <> ЭтотОбъект.Номер Тогда
        ЭтотОбъект.ДополнительныйРеквизитНомер = ЭтотОбъект.Номер;
    КонецЕсли;
    
КонецПроцедуры

Резюме: Чтобы избежать «звездочки» модифицированности и бесконечных циклов сохранения, всегда вносите изменения в данные объекта до того, как транзакция записи будет завершена. Использование УстановитьНовыйНомер() внутри ПередЗаписью() является самым практичным решением для управляемых форм 1С 8.3.

← На главную