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