При разработке и сопровождении конфигураций на платформе 1С 8.2 с использованием управляемых форм программисты часто сталкиваются с неожиданным поведением системы при попытке отменить проведение документа. В стандартной ситуации, если в процедуре ОбработкаПроведения мы устанавливаем параметр Отказ = Истина, ожидается, что система просто сообщит пользователю о том, что документ не проведен, и вернет управление без каких-либо ошибок. Однако, как показывает практика, при работе с управляемыми формами в версиях платформы 1С 8.2 (например, 8.2.17.169, 8.2.15 и других), вместо штатного сообщения о непроведении документа, мы можем получить сообщение об ошибке типа "Не удалось провести: "НазваниеДокумента"!" с кнопками "ОК" и "Подробно". При нажатии на "Подробно" выводится скупое "по причине: не удалось провести". Рассмотрим подробнее эту ситуацию, выясним ее причины и предложим возможные решения и обходные пути.
Наша цель, как правило, состоит в том, чтобы при определенных условиях (например, при незаполненности обязательных реквизитов или нарушениях логики учета) документ не проводился. Для этого в процедуре ОбработкаПроведения объекта документа мы пишем код, подобный следующему:
Процедура ОбработкаПроведения(Отказ, РежимПроведения)
// Предположим, у нас есть некоторая проверка
Если Не ПроверитьУсловияПроведения() Тогда
Отказ = Истина;
// Оператор Возврат; часто используется для немедленного завершения процедуры,
// но сам по себе он не является причиной ошибки, как мы выясним далее.
Возврат;
КонецЕсли;
// ... далее идет код формирования движений, если Отказ = Ложь
КонецПроцедуры
В обычных формах или при проведении из форм списка (как обычных, так и управляемых) этот код работает как ожидалось: документ не проводится, и пользователю выводится стандартное системное сообщение о том, что проведение не удалось, без какого-либо намека на ошибку или исключение. Однако, когда мы пытаемся провести тот же документ непосредственно из его управляемой формы объекта, система ведет себя иначе. Вместо обычного сообщения мы видим окно с заголовком "Ошибка при проведении", содержащее текст "Не удалось провести: " и кнопки "ОК" и "Подробно". При этом в отладчике мы видим, что параметр Отказ корректно устанавливается в Истина, и процедура ОбработкаПроведения завершается без исключений. Исключение возникает уже после выхода из процедуры, на уровне платформы.
Важно отметить, что эта проблема не связана с ошибками в коде самой процедуры ОбработкаПроведения. Мы можем воспроизвести ее, создав абсолютно новый, пустой документ без реквизитов, подписок и регистров, и просто поместив две строки Отказ = Истина; Возврат; в его процедуру ОбработкаПроведения. Проведение такого документа из его управляемой формы вызовет описанную ошибку, тогда как из формы списка или обычной формы – штатное сообщение.
Подобное поведение, когда установка Отказ = Истина приводит к исключению вместо стандартного сообщения, скорее всего, является особенностью реализации платформы 1С 8.2 для управляемых форм. Некоторые пользователи и статьи подтверждают, что такое поведение является "стандартным" для управляемых форм в толстом клиенте. Это может быть связано с тем, как платформа обрабатывает отмену проведения в различных режимах (обычные формы против управляемых) и с тем, что в управляемом приложении система может рассматривать отказ от проведения как исключение, если явно не предоставлено информативное сообщение пользователю.
Дело в том, что архитектура управляемых форм предполагает более строгое разделение между клиентской и серверной частями, а также более явную обработку ошибок и исключений. В отличие от обычных форм, где многие действия могли выполняться более "неявно", управляемые формы стремятся к тому, чтобы любые незапланированные события или отмены были корректно обработаны и визуально представлены пользователю. В данном случае платформа, вероятно, интерпретирует отказ от проведения без явного сообщения пользователю как нештатную ситуацию, требующую вызова исключения.
Ключевым наблюдением, которое помогает понять природу проблемы, является различие в поведении системы при проведении документа из разных форм:
Это наблюдение подтверждает, что причина кроется именно в специфике взаимодействия между управляемой формой документа и механизмом проведения. Очевидно, что контекст вызова процедуры проведения (из какой формы был инициирован процесс) имеет значение для платформы 1С 8.2 при обработке параметра Отказ.
Согласно стандартам и методикам разработки 1С, устанавливать параметр Отказ = Истина без информирования пользователя о причинах является неприемлемым. В таких случаях сообщение об ошибке (например, "Не удалось записать "Заказ покупателя"!") становится неинформативным и затрудняет расследование проблемы. Рекомендуется корректно уведомить пользователя о причинах отказа с помощью сообщения или вызова исключения с подробным описанием. Это не только улучшает пользовательский опыт, но и, как мы увидим далее, может помочь платформе корректнее обработать ситуацию.
Поскольку проблема является особенностью платформы, а не ошибкой в нашем коде, нам приходится искать обходные пути. Рассмотрим несколько подходов, которые помогут либо избежать этой ошибки, либо сделать ее обработку более понятной для пользователя.
Вместо простого Отказ = Истина, рекомендуется использовать функции Сообщить() или ПоказатьПредупреждение() для информирования пользователя о причине непроведения. Это соответствует стандартам разработки 1С и может помочь платформе корректнее обработать ситуацию, даже если исключение все равно возникает, пользователь будет знать причину.
Процедура ОбработкаПроведения(Отказ, РежимПроведения)
// Пример: проверка заполненности обязательного реквизита
Если Не ЗначениеЗаполнено(ЭтотОбъект.ВажныйРеквизит) Тогда
Отказ = Истина;
// Сообщаем пользователю о причине отмены проведения
Сообщить("Документ не проведен! Не заполнен обязательный реквизит 'ВажныйРеквизит'.", СтатусСообщения.Важное);
Возврат;
КонецЕсли;
// Пример: проверка баланса или других условий
Если Не ПроверитьДостаточностьСредств(ЭтотОбъект) Тогда
Отказ = Истина;
ПоказатьПредупреждение(, "Недостаточно средств на счете. Проведение документа отменено.");
Возврат;
КонецЕсли;
// ... остальной код проведения
КонецПроцедуры
Использование явных сообщений улучшает обратную связь с пользователем, что является лучшей практикой. В некоторых случаях, добавление Сообщить() может даже изменить поведение платформы, и она перестанет выкидывать исключение, ограничившись стандартным уведомлением. Однако это не гарантировано для всех версий и ситуаций.
Если платформа все равно генерирует исключение, можно попробовать обернуть вызов проведения документа в конструкцию Попытка...Исключение на клиентской стороне. Это позволит перехватить ошибку и вывести пользователю собственное, более информативное сообщение.
Разберем по шагам, как это можно реализовать:
Попытка...Исключение.Исключение мы можем вывести пользователю понятное сообщение, а также, при необходимости, зарегистрировать подробности ошибки для анализа программистом.Посмотрим на пример:
&НаКлиенте
Процедура КомандаПровестиДокумент(Команда)
Попытка
// Вызываем серверную процедуру, которая занимается проведением
ПровестиДокументНаСервере();
ПоказатьОповещениеПользователя("Документ успешно проведен.",,,,,,,);
Исключение
// Если на сервере Отказ = Истина привел к исключению, оно будет перехвачено здесь.
// Выводим более дружелюбное сообщение.
ПоказатьПредупреждение(, "Документ не был проведен. Возможно, не выполнены необходимые условия. Пожалуйста, проверьте заполнение реквизитов или обратитесь к администратору.");
// Для отладки можно вывести подробности:
// Сообщить("Подробности ошибки: " + ОписаниеОшибки(), СтатусСообщения.Важное);
КонецПопытки;
КонецПроцедуры
&НаСервереБезКонтекста
Процедура ПровестиДокументНаСервере()
// Получаем объект документа из реквизита формы
ДокументОбъект = РеквизитФормыВЗначение("Объект");
// Выполняем запись с режимом проведения
Попытка
ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
Исключение
// Если в ОбработкаПроведения на сервере Отказ = Истина
// вызывает исключение, мы можем его либо обработать здесь,
// либо, как в данном случае, просто пробросить на клиент.
// В зависимости от логики, можно дополнительно логировать.
ВызватьИсключение(ОписаниеОшибки()); // Пробрасываем исключение на клиент
КонецПопытки;
КонецПроцедуры
Такой подход дает нам полный контроль над тем, как ошибка будет представлена пользователю, скрывая от него технические детали и предлагая понятные инструкции.
Один из участников форума предложил временное решение, связанное с изменением режима совместимости интерфейса. Этот метод, хоть и не решает коренную проблему, позволяет избавиться от кнопки "Подробно" в сообщении об ошибке, делая его менее "страшным" для пользователя.
Последовательность действий следующая:
По словам автора, после этих манипуляций при проведении документа с Отказ = Истина в интерфейсе "Такси" вы будете получать окно ошибки без кнопки "Подробно". Это скорее обходной путь для визуального представления ошибки, а не системное решение самой причины ее возникновения. Его эффективность может зависеть от версии платформы и конфигурации.
Некоторые эксперты рекомендуют переносить проверки, которые могут привести к отмене проведения, в процедуру ПередЗаписью формы документа, особенно если речь идет о проверке заполненности реквизитов. В этой процедуре также есть параметр Отказ, который можно установить в Истина для предотвращения записи и, следовательно, проведения документа.
Преимущество этого подхода заключается в том, что если форма не записывается, то и попытка проведения не осуществляется. Это может быть более "чистым" способом отмены, поскольку мы пресекаем процесс на более ранней стадии, до того как система дойдет до вызова ОбработкаПроведения.
Рассмотрим пример:
&НаКлиенте
Процедура ПередЗаписью(Отказ, СтандартнаяОбработка)
// Проверяем заполненность реквизитов формы.
// Объект.ВажныйРеквизит - это ссылка на объект данных формы.
Если НЕ ЗначениеЗаполнено(Объект.ВажныйРеквизит) Тогда
Отказ = Истина;
ПоказатьПредупреждение(, "Не заполнен обязательный реквизит 'ВажныйРеквизит'. Запись и проведение документа отменены.");
Возврат;
КонецЕсли;
// Если необходимы более сложные проверки, требующие серверного контекста,
// их также можно выполнить здесь, вызвав серверную функцию.
// Пример:
// Если Не ВыполнитьСервернуюПроверкуНаЗапись() Тогда
// Отказ = Истина;
// ПоказатьПредупреждение(, "Документ не прошел серверную проверку перед записью.");
// КонецЕсли;
КонецПроцедуры
Важно понимать, что ПередЗаписью на клиенте отменяет запись формы. Если же нам требуется выполнить проверки на сервере перед записью/проведением, мы можем вызвать серверную процедуру из ПередЗаписьюНаСервере или из клиентской ПередЗаписью, если она требует доступа к контексту объекта.
&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, СтандартнаяОбработка)
// Здесь можно выполнить дополнительные серверные проверки,
// используя доступ к полям объекта и базам данных.
Если ЭтотОбъект.СуммаДокумента < 0 Тогда
Отказ = Истина;
Сообщить("Сумма документа не может быть отрицательной!", СтатусСообщения.Важное);
КонецЕсли;
КонецПроцедуры
Использование ПередЗаписью для таких проверок может быть более логичным с точки зрения архитектуры управляемых форм, поскольку оно позволяет пресечь некорректные данные до попытки их сохранения в базе и до инициирования сложного процесса проведения.
Проблема с выводом сообщения об ошибке вместо штатного уведомления при установке Отказ = Истина в процедуре ОбработкаПроведения для документов, открытых через управляемые формы в 1С 8.2, является известной особенностью платформы. Хотя это может показаться "багом", это, скорее, следствие специфической обработки событий в управляемом приложении.
Мы выяснили, что причина кроется не в ошибке нашего кода, а в поведении платформы, и что она проявляется только при проведении из управляемой формы объекта документа. Чтобы сделать работу с программой более комфортной для пользователя и избежать неинформативных сообщений об ошибках, мы рекомендуем:
Сообщить() или ПоказатьПредупреждение(), объясняющее причину отмены проведения. Это лучшая практика и часто помогает платформе корректнее обработать ситуацию.Попытка...Исключение, чтобы преобразовать системное исключение в дружелюбное пользовательское сообщение.ПередЗаписью формы, чтобы отменить запись до попытки проведения.Понимание этих особенностей платформы 1С 8.2 и применение предложенных подходов помогут вам создавать более надежные и удобные для пользователя решения.