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