Как устроены события платформы 1С:Предприятие 8.3 и где писать их обработчики

· 10 мин

Содержание

Событие в 1С — это момент, в который платформа сама вызывает процедуру с заранее известным именем: перед записью объекта, при открытии формы, при изменении поля, при старте сеанса. Разработчик не вызывает такую процедуру из кода. Он пишет обработчик в нужном модуле, а платформа запускает его в нужный момент и передаёт параметры, например Отказ.

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

Суть механизма: кто вызывает обработчик и что он может изменить

Обработчик события — обычная процедура со строго заданным именем и набором параметров. Имя и параметры определяет платформа, а не программист. Если ошибиться в имени (ПередЗапись вместо ПередЗаписью), процедура просто не вызовется, и сообщения об ошибке не будет.

Что даёт обработчик:

  • 🛑 Отменить действие. Почти у всех событий с приставкой «Перед…» есть параметр Отказ. Установили Отказ = Истина — запись, удаление или закрытие формы не произойдёт.
  • ✏️ Изменить данные. В ПередЗаписью можно дозаполнить реквизиты, и они запишутся вместе с объектом.
  • 🔁 Заменить стандартное поведение. Параметр СтандартнаяОбработка = Ложь отключает то, что платформа сделала бы сама, например стандартный выбор из списка.
  • 📣 Отреагировать постфактум. События без «Перед…» (ПриЗаписи, ПослеЗаписи, ПриЗакрытии) сообщают, что действие уже выполнено.

Обработчики объекта выполняются на сервере, обработчики формы — на клиенте или на сервере, в зависимости от события. Это видно по суффиксу: ПриСозданииНаСервере идёт на сервере, ПриОткрытии — на клиенте.

Виды событий в 1С и модули, где они живут

Удобнее всего классифицировать события по месту, где пишется обработчик. Каждому модулю соответствует свой набор событий.

МодульПримеры событийГде выполняется
Модуль объекта (справочник, документ)ПередЗаписью, ПриЗаписи, ОбработкаЗаполнения, ОбработкаПроверкиЗаполнения, ПередУдалением, ПриКопировании, ОбработкаПроведенияСервер
Модуль набора записей регистраПередЗаписью, ПриЗаписиСервер
Модуль менеджераОбработкаПолученияДанныхВыбора, ОбработкаПолученияФормы, ОбработкаПолученияПредставленияСервер
Модуль формыПриСозданииНаСервере, ПриОткрытии, ПередЗакрытием, ОбработкаОповещения, ПередЗаписью, ПослеЗаписиКлиент и сервер
Элемент формыПриИзменении, НачалоВыбора, ОбработкаВыбора, ОчисткаКлиент
Модуль сеансаУстановкаПараметровСеансаСервер
Модуль управляемого приложенияПередНачаломРаботыСистемы, ПриНачалеРаботыСистемы, ПередЗавершениемРаботыСистемы, ПриЗавершенииРаботыСистемыКлиент

Самый быстрый способ создать обработчик без опечаток — не писать его руками. В конфигураторе откройте модуль объекта и выберите событие в списке процедур над текстом модуля, либо в форме откройте свойства элемента и нажмите кнопку с лупой у нужного события на закладке событий. Конфигуратор сам создаст процедуру с правильным именем и параметрами.

Пример: запрет записи без контрагента

Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)

    Если ОбменДанными.Загрузка Тогда
        Возврат;
    КонецЕсли;

    Если Не ЗначениеЗаполнено(Контрагент) Тогда
        ОбщегоНазначения.СообщитьПользователю("Не указан контрагент", ЭтотОбъект, "Контрагент");
        Отказ = Истина;
    КонецЕсли;

КонецПроцедуры

Модуль ОбщегоНазначения есть в типовых конфигурациях на БСП. В своей конфигурации без БСП используйте объект СообщениеПользователю.

Проверку заполненности обязательных реквизитов лучше делать не в ПередЗаписью, а в ОбработкаПроверкиЗаполнения или через свойство реквизита «Проверка заполнения». Тогда платформа сама подсветит поле в форме.

Порядок событий при записи и проведении документа

Чаще всего путаница возникает с вопросом «какой обработчик сработает раньше». Для документа, который пользователь записывает и проводит из формы, порядок такой:

  1. ПередЗаписью — модуль формы, клиент. Можно отменить запись ещё до обращения к серверу.
  2. Проверка заполнения: ОбработкаПроверкиЗаполнения в модуле объекта и ОбработкаПроверкиЗаполненияНаСервере в форме.
  3. ПередЗаписьюНаСервере — модуль формы. Есть параметр ТекущийОбъект, это уже объект, а не данные формы.
  4. ПередЗаписью — модуль объекта. Отсюда начинается транзакция.
  5. ПриЗаписи — модуль объекта. Данные документа уже записаны в базу внутри транзакции.
  6. ОбработкаПроведения — модуль объекта, формирование движений по регистрам.
  7. ПриЗаписиНаСервере и ПослеЗаписиНаСервере — модуль формы.
  8. ПослеЗаписи — модуль формы, клиент.

Если на любом шаге с 4-го по 6-й выставить Отказ = Истина, транзакция откатится целиком: ни документ, ни движения не запишутся.

При записи из кода (ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение)) форменных событий нет: выполняются только шаги 4–6 и подписки.

Открытие формы существующего объекта идёт так: ПриЧтенииНаСервере → ПриСозданииНаСервере → ПриОткрытии. Закрытие: ПередЗакрытием → ПриЗакрытии.

Подписки на события: один обработчик для многих объектов

Подписка на событие — объект конфигурации в ветке Общие → Подписки на события. У неё три главных свойства:

  • Источник — список типов объектов, на которые она реагирует, например ДокументОбъект.РеализацияТоваровУслуг и ДокументОбъект.ПоступлениеТоваровУслуг.
  • Событие — какое событие перехватывать: ПередЗаписью, ПриЗаписи, ОбработкаПроведения, ПередУдалением и другие.
  • Обработчик — экспортная процедура общего модуля.

Как создать подписку

  1. Создайте общий модуль, например ПодпискиКонтроль. Отметьте флажок «Сервер», флажки «Клиент» и «Вызов сервера» не нужны.
  2. В ветке Общие → Подписки на события добавьте новый объект.
  3. В свойстве «Источник» отметьте нужные типы объектов.
  4. В свойстве «Событие» выберите событие.
  5. В свойстве «Обработчик» укажите процедуру. Если её ещё нет, конфигуратор предложит создать её в выбранном модуле с правильными параметрами.

Пример обработчика подписки

Процедура КонтрольДатыПередЗаписью(Источник, Отказ, РежимЗаписи, РежимПроведения) Экспорт

    Если Источник.ОбменДанными.Загрузка Тогда
        Возврат;
    КонецЕсли;

    Если Источник.Дата > КонецДня(ТекущаяДатаСеанса()) Тогда
        Отказ = Истина;
        ВызватьИсключение "Нельзя записать документ будущей датой";
    КонецЕсли;

КонецПроцедуры

Первый параметр Источник — сам объект, который записывается. Остальные параметры повторяют параметры события в модуле объекта.

Ключевые правила подписок:

  • ⏱ Обработчик подписки вызывается после обработчика с тем же именем в модуле объекта.
  • 🔀 Порядок вызова нескольких подписок на одно событие платформа не гарантирует. Не стройте логику, где одна подписка зависит от результата другой.
  • 📦 Типовые конфигурации активно используют подписки: регистрация изменений для обменов, версионирование объектов, контроль дат запрета изменения. Свою подписку проще добавить через расширение, чем менять типовую конфигурацию .

События формы: от создания до изменения поля

События формы делятся на два уровня: события самой формы и события её элементов.

События формы

  • 🟢 ПриСозданииНаСервере(Отказ, СтандартнаяОбработка) — первое событие. Здесь заполняют реквизиты формы, настраивают видимость элементов, читают параметры открытия через Параметры.
  • 🟢 ПриОткрытии(Отказ) — клиент, форма вот-вот появится на экране. Подходит для подключения обработчиков ожидания.
  • 🟡 ПередЗакрытием(Отказ, ЗавершениеРаботы, ТекстПредупреждения, СтандартнаяОбработка) — можно спросить «Сохранить изменения?» или запретить закрытие.
  • 🔴 ПриЗакрытии(ЗавершениеРаботы) — форма уже закрывается, отменить нельзя.
  • 📣 ОбработкаОповещения(ИмяСобытия, Параметр, Источник) — приём сообщений от других форм.
  • ↩️ ОбработкаВыбора(ВыбранноеЗначение, ИсточникВыбора) — сюда возвращается значение из формы, открытой в режиме выбора.

События элементов

  • ПриИзменении(Элемент) — пользователь изменил значение поля и ушёл из него. Типичная задача: при смене контрагента подставить договор.
  • НачалоВыбора(Элемент, ДанныеВыбора, СтандартнаяОбработка) — нажата кнопка выбора. Здесь можно открыть свою форму вместо стандартной.
  • Очистка(Элемент, СтандартнаяОбработка) — пользователь очистил поле.
&НаКлиенте
Процедура КонтрагентПриИзменении(Элемент)
    КонтрагентПриИзмененииНаСервере();
КонецПроцедуры

&НаСервере
Процедура КонтрагентПриИзмененииНаСервере()
    Объект.Договор = Справочники.ДоговорыКонтрагентов.ПустаяСсылка();
КонецПроцедуры

ПриИзменении срабатывает только при интерактивном изменении. Если значение поля поменял код (Объект.Контрагент = ...), обработчик не вызовется — его нужно вызвать вручную.

Обработчик элементу можно назначить и программно, например у поля, созданного в ПриСозданииНаСервере:

НовоеПоле.УстановитьДействие("ПриИзменении", "ДинамическоеПолеПриИзменении");

Процедура с таким именем должна существовать в модуле формы с директивой &НаКлиенте.

Свои события: Оповестить, ОбработкаОповещения и расширения

Объявить «новое событие» в конфигураторе, как в объектных языках, нельзя. Но есть три штатных способа построить собственный событийный обмен.

1. Оповещение между формами

Форма-отправитель вызывает глобальный метод на клиенте:

&НаКлиенте
Процедура ПослеЗаписи(ПараметрыЗаписи)
    Оповестить("ЗаписанЗаказ", Объект.Ссылка, ЭтаФорма);
КонецПроцедуры

Все открытые формы получают вызов ОбработкаОповещения. Каждая сама решает, реагировать ли:

&НаКлиенте
Процедура ОбработкаОповещения(ИмяСобытия, Параметр, Источник)
    Если ИмяСобытия = "ЗаписанЗаказ" Тогда
        Элементы.Список.Обновить();
    КонецЕсли;
КонецПроцедуры

Имя события — произвольная строка. Договоритесь о понятных именах и храните их в одном месте, иначе через год никто не найдёт, кто шлёт «Обновить1».

Оповещение работает только в пределах клиентского приложения одного пользователя. Форма другого пользователя его не получит.

2. Описание оповещения для асинхронных вызовов

ОписаниеОповещения — это «обработчик, который вызовется потом»: после ответа на вопрос, закрытия формы, выбора файла.

Оповещение = Новый ОписаниеОповещения("ПослеОтветаНаВопрос", ЭтотОбъект);
ПоказатьВопрос(Оповещение, "Перезаполнить табличную часть?", РежимДиалогаВопрос.ДаНет);

Процедура ПослеОтветаНаВопрос(Результат, ДополнительныеПараметры) получит выбор пользователя.

3. Перехват событий в расширении

Расширение конфигурации (платформа 8.3.6 и выше) умеет добавить свою логику к событию типового объекта, не снимая конфигурацию с поддержки. Для этого используют аннотации:

&После("ПередЗаписью")
Процедура Расш1_ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    // своя проверка после типового кода
КонецПроцедуры
  • &Перед — ваш код выполнится до типового обработчика.
  • &После — после типового.
  • &Вместо — вместо типового; вызвать исходный код можно через ПродолжитьВызов(...).

Это главный приём практика: доработка обработчика через расширение переживает обновление типовой конфигурации, правка модуля объекта — нет.

Ошибки и подводные камни при работе с событиями

Диагностика: выберите симптом

Ещё несколько нюансов, которые не видны сразу:

  • 🔄 Рекурсия. Вызов Записать() того же объекта внутри его ПриЗаписи запускает событие снова. Нужно менять данные — делайте это в ПередЗаписью, там запись ещё не произошла.
  • 🧱 Транзакция. Всё, что выполняется в ПередЗаписью, ПриЗаписи и ОбработкаПроведения, — внутри одной транзакции. Долгий HTTP-запрос к внешнему сервису в этих обработчиках держит блокировки и тормозит других пользователей.
  • 🧭 Контекст. В модуле объекта нет доступа к форме, а в клиентском обработчике формы нет доступа к базе. Данные с сервера на клиент передавайте через серверную процедуру формы.
  • 👥 Права. Обработчик выполняется с правами текущего пользователя. Если нужно прочитать данные, к которым у него нет доступа, используйте привилегированный режим или привилегированный общий модуль.

Практические сценарии: какое событие выбрать под задачу

ЗадачаСобытиеМодуль
Заполнить новый документ значениями по умолчаниюОбработкаЗаполненияМодуль объекта
Заполнить документ на основании другогоОбработкаЗаполнения, параметр ДанныеЗаполненияМодуль объекта
Запретить запись с пустым или неверным реквизитомОбработкаПроверкиЗаполненияМодуль объекта
Очистить служебные поля при копированииПриКопированииМодуль объекта
Сформировать движения по регистрамОбработкаПроведенияМодуль объекта
Запретить удаление элемента, который где-то используетсяПередУдалениемМодуль объекта
Одна проверка для 20 видов документовПодписка на ПередЗаписьюОбщий модуль
Подставить договор при смене контрагентаПриИзменении поляМодуль формы
Обновить список после записи документа в другой формеОповестить + ОбработкаОповещенияМодули форм
Отфильтровать выпадающий список при вводе по строкеОбработкаПолученияДанныхВыбораМодуль менеджера
Заполнить параметры сеанса при входеУстановкаПараметровСеансаМодуль сеанса
Показать предупреждение при запуске программыПриНачалеРаботыСистемыМодуль управляемого приложения

Мини-кейс: автозаполнение ответственного

Задача: в каждом новом документе поле «Ответственный» должно заполняться текущим пользователем. Документов 15.

Плохое решение — 15 одинаковых обработчиков ОбработкаЗаполнения. Хорошее — одна подписка на ОбработкаЗаполнения с этими 15 документами в «Источнике» и обработчик в общем модуле:

Процедура ЗаполнитьОтветственного(Источник, ДанныеЗаполнения, ТекстЗаполнения, СтандартнаяОбработка) Экспорт
    Если Не ЗначениеЗаполнено(Источник.Ответственный) Тогда
        Источник.Ответственный = Пользователи.ТекущийПользователь();
    КонецЕсли;
КонецПроцедуры

Пользователи.ТекущийПользователь() — функция БСП. Добавился 16-й документ — отметили его в подписке, код не трогаете.

Частые вопросы о событиях 1С

Чем событие отличается от обычной процедуры?
Обычную процедуру вызывает ваш код. Обработчик события вызывает платформа, когда наступает определённый момент, и передаёт ему параметры вроде Отказ.
Срабатывают ли события при записи через обработку или COM-соединение?
Да. События модуля объекта и подписки вызываются при любой записи, в том числе из кода и внешнего соединения. Не вызываются только события формы.
Можно ли отключить подписку, не удаляя её?
Штатного флажка «выключено» у подписки нет. Обычно в обработчик добавляют проверку функциональной опции или константы и выходят из процедуры, если она выключена.
Что значит «событие» в журнале регистрации?
Это другое понятие: запись о действии пользователя или системы (вход, запись данных, ошибка). Своё событие журнала пишут методом ЗаписьЖурналаРегистрации, к обработчикам событий объектов оно не относится.
Где посмотреть, какие подписки висят на документе?
В конфигураторе откройте документ, вызовите контекстное меню и выберите поиск ссылок на объект. В результатах будут подписки, у которых документ указан в «Источнике».