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