Как правильно вывести предупреждение пользователю при проведении документа в 1С (Обычные формы)

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

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

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

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

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

Решение 1: Использование коллекции ДополнительныеСвойства

Рассмотрим наиболее надежный и правильный способ передачи текстового сообщения из «недр» проведения обратно на форму документа. Для этого мы воспользуемся встроенной коллекцией ДополнительныеСвойства, которая есть у каждого объекта документа.

Шаг 1. Запись сообщения в модуле объекта

Внутри процедуры ОбработкаПроведения (или в подписке на событие, куда передается объект документа) мы не вызываем окно, а просто сохраняем текст в коллекцию:


Процедура ОбработкаПроведения(Отказ, РежимПроведения)
    // Какие-то ваши проверки и логика
    Если НужноВывестиПредупреждение Тогда
        ДополнительныеСвойства.Вставить("ТекстДляПользователя", "Внимание: Обнаружено расхождение в остатках, проверьте данные!");
    КонецЕсли;
КонецПроцедуры

Шаг 2. Перехват сообщения в модуле формы

Теперь нам нужно извлечь это сообщение после того, как транзакция проведения завершилась, и данные были успешно записаны в базу. Для этого идеально подходит событие формы ПослеЗаписи. Разберем код:


Процедура ПослеЗаписи()
    Перем ТекстПредупреждения;
    
    // Проверяем, есть ли в объекте наше сообщение
    Если ЭтотОбъект.ДополнительныеСвойства.Свойство("ТекстДляПользователя", ТекстПредупреждения) Тогда
        // Теперь мы находимся на клиенте, транзакция закрыта, блокировок нет
        ПоказатьПредупреждение(, ТекстПредупреждения, 60, "Результат проведения");
        
        // Не забываем очистить свойство, чтобы оно не вывелось повторно при следующем сохранении
        ЭтотОбъект.ДополнительныеСвойства.Удалить("ТекстДляПользователя");
    КонецЕсли;
КонецПроцедуры

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

Решение 2: Использование объекта СообщениеПользователю

Посмотрим на более современный метод, который поддерживается в версиях платформы 8.2 и 8.3 даже для обычных форм. Это использование объекта СообщениеПользователю. Этот метод хорош тем, что он не требует написания кода на форме и не останавливает выполнение программы.

Рассмотрим пример кода в общем модуле:


Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "Документ проведен с особенностями в части взаиморасчетов.";
Сообщение.УстановитьДанные(ЭтотОбъект); // Привязка сообщения к объекту
Сообщение.Сообщить();

Особенности этого метода:

  1. Сообщения накапливаются во время транзакции и выводятся пользователю в специальное окно сообщений автоматически после завершения процесса.
  2. Если вы используете УстановитьДанные(), платформа может подсветить поле, к которому относится сообщение (если оно есть на форме).
  3. Метод безопасен при серверных вызовах и программном проведении.

Решение 3: Директивы препроцессора (быстрый, но опасный путь)

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

Проанализируем пример:


#Если Клиент Тогда
    ПоказатьПредупреждение(, "Сообщение только для клиента!");
#КонецЕсли

Важное предостережение: использование этого метода внутри ОбработкаПроведения все еще является плохой практикой из-за упоминавшихся ранее блокировок. Используйте его только в исключительных случаях и только для кода, который гарантированно выполняется вне транзакций записи.

Рекомендации по проектированию

Рассмотрим ситуацию с другой стороны: если цель вашего предупреждения — остановить проведение, потому что данные некорректны, то лучше использовать событие ОбработкаПроверкиЗаполнения. Оно срабатывает до начала транзакции проведения. Это позволяет выдать ошибку, установить параметр Отказ = Истина и не нагружать базу данных лишними операциями.

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

← На главную