Как изменить реквизит документа при печати из внешней формы и избежать ошибки «Объект изменен»

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

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

Выясним причину возникновения ошибки

Для начала разберем внутренний механизм работы управляемого приложения. Когда пользователь открывает форму документа, платформа считывает данные из базы и сохраняет их в оперативной памяти (в объекте формы). Вместе с данными считывается и версия данных (скрытое поле DataVersion).

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

Способ 1. Использование механизма оповещений (Метод ОповеститьОбИзменении)

Это наиболее элегантный способ, если мы хотим оставить логику записи внутри печатной формы. Суть заключается в том, чтобы заставить открытую форму документа «перечитать» данные из базы сразу после того, как печатная форма их изменила.

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

  1. В модуле внешней печатной формы (в процедуре печати на сервере) мы находим нужный объект, меняем реквизит и записываем его.
  2. Сразу после записи на стороне клиента необходимо вызвать метод ОповеститьОбИзменении(Ссылка).

Пример кода в модуле внешней обработки:


// На сервере выполняем запись
ОбъектДок = СсылкаНаДокумент.ПолучитьОбъект();
ОбъектДок.Распечатано = Истина;
ОбъектДок.Записать();

// На клиенте (в процедуре, вызывающей печать)
ОповеститьОбИзменении(СсылкаНаДокумент);

В самой форме документа (если это типовая конфигурация на БСП) уже может быть заложен механизм реакции на оповещения. Если же форма самописная, нам нужно в обработчике ОбработкаОповещения вызвать метод Прочитать(). Это обновит данные в форме и синхронизирует версии данных, исключая ошибку при последующем сохранении.

Способ 2. Использование регистра сведений вместо реквизита документа

Многие специалисты рекомендуют этот метод как самый надежный с точки зрения архитектуры 1С. Вместо добавления реквизита Распечатано непосредственно в документ, мы создаем независимый Регистр сведений (например, СтатусыПечатиДокументов), где измерением будет ссылка на документ, а ресурсом — флаг или дата печати.

Разберем преимущества этого подхода:

Пример записи в регистр из кода печати:


МенеджерЗаписи = РегистрыСведений.СтатусыПечатиДокументов.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Документ = СсылкаНаОбъект;
МенеджерЗаписи.ДатаПечати = ТекущаяДата();
МенеджерЗаписи.Пользователь = ПараметрыСеанса.ТекущийПользователь;
МенеджерЗаписи.Записать();

Способ 3. Делегирование записи самой форме (через БСП)

Если конфигурация использует Библиотеку стандартных подсистем (БСП), мы можем воспользоваться механизмом обратных вызовов. Вместо того чтобы пытаться записать объект «втихаря» от формы, мы можем передать сигнал форме, что она должна сама обновить свой статус.

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

Способ 4. Использование расширений и «Дополнительных свойств»

Иногда требуется жесткий контроль: если документ распечатан, его нельзя менять никому, кроме администратора. В этом случае удобно использовать Расширение конфигурации. Рассмотрим пошагово:

  1. В расширении перехватываем обработчик ПередЗаписью документа.
  2. Проверяем значение реквизита Распечатано.
  3. Если документ уже помечен как распечатанный и текущий пользователь не имеет роли ПолныеПрава, отменяем транзакцию с выводом предупреждения.

Для того чтобы сама печатная форма могла обойти этот запрет при первой установке флага, используем коллекцию ДополнительныеСвойства:


// В печатной форме перед записью
ОбъектДок = СсылкаНаДокумент.ПолучитьОбъект();
ОбъектДок.ДополнительныеСвойства.Вставить("РазрешитьЗаписьРаспечатанного", Истина);
ОбъектДок.Распечатано = Истина;
ОбъектДок.Записать();

А в модуле объекта (или расширении) проверяем это свойство. Если оно установлено — пропускаем запись, даже если сработали триггеры блокировки изменений.

Резюме и рекомендации

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

Помните, что манипуляции с версией данных (DataVersion) напрямую через СУБД или попытки «подменить» её в памяти являются опасными и могут привести к непредсказуемому поведению системы. Рекомендуется использовать штатные механизмы платформы 1С:Предприятие 8.3.

← На главную