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