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