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