Где и как срабатывает код 1С: события объектов, форм, подписки и обмены

· 11 мин

Содержание

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

Виды обработчиков различаются по тому, где лежит код и что его запускает. Событие объекта (ПередЗаписью, ОбработкаПроведения) живёт в модуле объекта. Событие формы (ПриСозданииНаСервере, ПриИзменении) — в модуле формы. Подписка на событие позволяет повесить один обработчик на десятки объектов сразу.

Что такое обработчик в 1С и чем он отличается от обработки

Новички путают два слова. Обработка — это объект конфигурации: отдельная форма с кнопками, например «Групповое перепроведение документов » или внешняя .epf. Обработчик — это процедура, привязанная к событию. Внутри обработки, кстати, тоже есть обработчики: нажатие кнопки на её форме — это событие, и код под ним — обработчик команды.

Главное свойство обработчика — его вызывает платформа. Отсюда три следствия:

  • 📌 Имя и параметры фиксированы. Процедура ПередЗаписью(Отказ) в модуле справочника сработает. Процедура ДоЗаписи(Отказ) — нет, платформа её не знает.
  • 📌 Контекст фиксирован. Обработчик ПриОткрытии формы выполняется на клиенте, ПриСозданииНаСервере — на сервере. Перепутать директиву компиляции нельзя.
  • 📌 Можно отменить действие. У многих событий есть параметр Отказ. Установили Отказ = Истина — запись, проведение или закрытие формы не состоится.

Если код «не срабатывает», первым делом сверьте имя процедуры и её место. Обработчик формы в модуле объекта просто не будет вызван, и никакой ошибки вы не увидите.

Виды обработчиков в 1С: от модуля объекта до регламентных заданий

В 1С 8.3 набор механизмов событий довольно широкий. Удобно разложить их по месту, где пишется код.

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

Ещё один слой — обработчики модуля приложения и модуля сеанса: ПередНачаломРаботыСистемы, ПриНачалеРаботыСистемы, УстановкаПараметровСеанса. Они срабатывают при входе пользователя в базу.

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

Порядок важно знать, чтобы положить проверку в правильное место. При проведении документа из формы цепочка выглядит так:

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

Всё с 3 по 6 шаг идёт в одной транзакции. Отказ = Истина на любом из них откатывает запись целиком.

Как создать обработчик в 1С: пошагово в Конфигураторе

Обработчик создают в Конфигураторе или в 1С:EDT. Порядок для Конфигуратора такой.

Обработчик события объекта

  1. Откройте дерево конфигурации и найдите объект, например документ «Реализация товаров и услуг».
  2. В контекстном меню объекта откройте модуль объекта.
  3. Напишите процедуру с точным именем события и тем же набором параметров, что указан в синтакс-помощнике для этого объекта. Лишний или пропущенный параметр — ошибка при вызове.
  4. Добавьте код и сохраните конфигурацию: F7 — обновить конфигурацию базы данных.
Процедура ПередЗаписью(Отказ)

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

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

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

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

Обработчик события формы или элемента

  1. Откройте форму в редакторе.
  2. Выделите форму (корневой элемент) или нужное поле.
  3. В палитре свойств найдите группу «События».
  4. Нажмите кнопку с лупой справа от события, например «ПриИзменении».
  5. Платформа спросит, где создать обработчик: на клиенте, на клиенте с серверной процедурой без контекста и т. д. Выберите вариант — заготовка появится в модуле формы, а её имя пропишется в свойстве события.

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

Подписка на событие

  1. В дереве конфигурации раскройте ветку «Общие» → «Подписки на события» и добавьте новую подписку.
  2. В свойстве «Источник» отметьте типы объектов: например, все справочники или только «Контрагенты» и «Организации».
  3. В свойстве «Событие» выберите событие: ПередЗаписью, ПриЗаписи, ОбработкаПроверкиЗаполнения и др.
  4. В свойстве «Обработчик» укажите экспортную процедуру серверного общего модуля.
// Общий модуль ПодпискиПроверкиСервер (Сервер = Истина)
Процедура ПроверитьКомментарийПередЗаписью(Источник, Отказ) Экспорт

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

    Если СтрДлина(Источник.Комментарий) > 500 Тогда
        Отказ = Истина;
    КонецЕсли;

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

Первый параметр подписки всегда Источник — сам объект. Дальше идут параметры события в том же порядке, что и в модуле объекта.

Доработка типовой: обработчики в расширении

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

  • &Перед("ПередЗаписью") — ваш код выполнится до типового;
  • &После("ПередЗаписью") — после типового;
  • &Вместо("ПередЗаписью") — заменяет типовой; вызвать исходный можно через ПродолжитьВызов().

Такой обработчик переживает обновление типовой, если не изменилась сигнатура исходной процедуры.

Обработчики событий формы: клиент, сервер и асинхронность

Форма работает в двух контекстах, и обработчики разделены между ними. Грубая схема:

  • 🖥 Сервер: ПриСозданииНаСервере, ПриЧтенииНаСервере, ПередЗаписьюНаСервере, ПослеЗаписиНаСервере, ОбработкаПроверкиЗаполненияНаСервере.
  • 💻 Клиент: ПриОткрытии, ПередЗакрытием, ОбработкаОповещения, ОбработкаВыбора, события элементов (ПриИзменении, НачалоВыбора).

ПриСозданииНаСервере срабатывает один раз, до показа формы. Здесь ставят видимость элементов, заполняют реквизиты формы по параметрам открытия. ПриОткрытии — уже на клиенте, когда форма появилась.

Как вызывать сервер из события элемента

Пример: пользователь выбрал номенклатуру, нужно подставить цену.

&НаКлиенте
Процедура ТоварыНоменклатураПриИзменении(Элемент)
    ТекущиеДанные = Элементы.Товары.ТекущиеДанные;
    ТекущиеДанные.Цена = ЦенаНоменклатуры(ТекущиеДанные.Номенклатура, Объект.Дата);
    ТекущиеДанные.Сумма = ТекущиеДанные.Цена * ТекущиеДанные.Количество;
КонецПроцедуры

&НаСервереБезКонтекста
Функция ЦенаНоменклатуры(Номенклатура, Дата)
    // запрос к регистру цен
    Возврат 0;
КонецФункции

Директива &НаСервереБезКонтекста не передаёт на сервер всю форму. На табличной части в 300 строк разница в скорости заметна каждому пользователю.

Вопрос пользователю: обработчик оповещения

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

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

&НаКлиенте
Процедура ОчиститьПослеВопроса(Результат, ДополнительныеПараметры) Экспорт
    Если Результат = КодВозвратаДиалога.Да Тогда
        Объект.Товары.Очистить();
    КонецЕсли;
КонецПроцедуры

Процедура-обработчик оповещения обязана быть экспортной. В новых версиях платформы тот же сценарий можно написать через Асинх и Ждать — код читается линейно, но под капотом это всё тот же отложенный вызов.

Обработчик ожидания

Таймер на форме включают методом ПодключитьОбработчикОжидания:

&НаКлиенте
Процедура ПриОткрытии(Отказ)
    ПодключитьОбработчикОжидания("ОбновитьСписокЗаказов", 60);
КонецПроцедуры

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

Третий параметр Истина делает вызов однократным. Остановить повторяющийся таймер — ОтключитьОбработчикОжидания("ОбновитьСписокЗаказов").

Обработчики при обмене данными и интеграции

Когда данные приходят из другой базы, срабатывают те же события объекта, что и при ручной записи. Разница — во флаге ОбменДанными.Загрузка. Если он установлен в Истина, платформа пропускает проверки, а разработчик в своих обработчиках должен сделать то же самое: выйти из ПередЗаписью и ПриЗаписи без бизнес-логики. Объект при загрузке пишется «как есть».

Плюс у механизмов обмена есть свои обработчики:

  • 🔄 Правила «Конвертации данных» 2.0 (формат XML -правил). Обработчики на уровне всего обмена — ПередВыгрузкойДанных, ПослеЗагрузкиДанных, и на уровне правила объекта — ПередВыгрузкой, ПриВыгрузке, ПослеВыгрузки, ПередЗагрузкой, ПриЗагрузке, ПослеЗагрузки. Код пишется прямо в правилах и выполняется обработкой «Универсальный обмен данными в формате XML» или механизмом обмена типовой.
  • 🔄 Обмен через формат EnterpriseData (типовые БП, УТ, ЗУП). Код обработчиков лежит в модуле менеджера обмена: события вроде ПриОтправкеДанных и ПриКонвертацииДанныхXDTO преобразуют объект в формат и обратно.
  • 🔄 Планы обмена. Объекты регистрируются к выгрузке автоматически по составу плана обмена. В ПередЗаписью объекта можно поправить ОбменДанными.Получатели, чтобы не отправлять конкретный объект конкретному узлу.

HTTP-сервис как обработчик

Для интеграции с сайтом или внешней системой в конфигурации создают HTTP-сервис. На каждый метод шаблона URL (GET, POST) назначается функция-обработчик:

Функция ОстаткиGET(Запрос)
    Артикул = Запрос.ПараметрыURL["article"];
    Ответ = Новый HTTPСервисОтвет(200);
    Ответ.Заголовки.Вставить("Content-Type", "application/json; charset=utf-8");
    Ответ.УстановитьТелоИзСтроки("{""article"": """ + Артикул + """, ""qty"": 0}");
    Возврат Ответ;
КонецФункции

Сервис начинает отвечать после публикации базы на веб-сервере. В рабочем коде JSON собирают через ЗаписьJSON, а не склеиванием строк — так не сломаются кавычки в артикуле.

Куда положить код, чтобы он срабатывал как нужно?

Типичные ошибки в обработчиках и как их не допустить

Эти ошибки встречаются в реальных доработках и не всегда видны сразу.

  • ❌ Нет проверки ОбменДанными.Загрузка. Обмен начинает падать на ваших проверках или, хуже, пересчитывает данные, пришедшие из центральной базы. Первая строка ПередЗаписью, ПриЗаписи и подписок — выход при загрузке.
  • ❌ Изменение реквизитов в ПриЗаписи. Объект уже записан. Присваивание ЭтотОбъект.Комментарий = ... в этом обработчике в базу не попадёт. Менять данные нужно в ПередЗаписью.
  • ❌ Записать() внутри своего же события. Вызов записи объекта из его ПриЗаписи даёт рекурсию. Если нужно что-то передать между событиями, используйте ДополнительныеСвойства: ДополнительныеСвойства.Вставить("ПропуститьПроверку", Истина).
  • ❌ Вызов сервера на каждое нажатие клавиши. Серверный вызов в ИзменениеТекстаРедактирования по полю поиска тормозит форму. Добавьте обработчик ожидания с задержкой в полсекунды и однократным вызовом.
  • ❌ Подписка на процедуру без Экспорт или в клиентском общем модуле. Подписка не сработает.
  • ❌ Таймер, который не отключили. Обработчик ожидания с интервалом 1 секунда и серверным вызовом внутри при 50 открытых формах — это 50 обращений к серверу в секунду.
  • ❌ Тяжёлые запросы в ПриЗаписи и ОбработкаПроведения. Транзакция держит блокировки, пока обработчик не закончит. Длинный обработчик проведения у одного пользователя превращается в ожидание на блокировке у остальных.

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

Для отладки поставьте точку останова (F9) на первой строке обработчика и запустите базу в режиме отладки (F5) из Конфигуратора. Если точка не срабатывает — значит, событие не вызывается вообще, и проблема в привязке, а не в коде.

Примеры обработчиков из практики

Несколько задач, которые закрываются одним обработчиком.

Запрет редактирования закрытого периода

// Модуль документа
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    Если ОбменДанными.Загрузка Тогда
        Возврат;
    КонецЕсли;
    Если Дата < '20260101' Тогда
        Сообщение = Новый СообщениеПользователю;
        Сообщение.Текст = "Период закрыт. Изменения запрещены.";
        Сообщение.Сообщить();
        Отказ = Истина;
    КонецЕсли;
КонецПроцедуры

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

Заполнение нового документа по основанию

Событие ОбработкаЗаполнения(ДанныеЗаполнения, ТекстЗаполнения, СтандартнаяОбработка) в модуле объекта срабатывает при вводе на основании и при создании через Заполнить(). Проверяете тип ДанныеЗаполнения и копируете нужные реквизиты: контрагента, договор, табличную часть.

Обязательное поле только при условии

Событие ОбработкаПроверкиЗаполнения(Отказ, ПроверяемыеРеквизиты) позволяет управлять списком обязательных реквизитов. Например, «Договор» обязателен, только если вид операции — продажа комиссионеру:

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

Имена перечисления и значений в примере условные — в вашей конфигурации они свои.

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

Можно ли повесить два обработчика на одно событие объекта?
Да. В модуле объекта процедура с именем события одна, но подписок на то же событие может быть сколько угодно, плюс обработчики в расширениях. Порядок вызова подписок между собой не гарантирован — не стройте на нём логику.
Чем `ОбработкаПроверкиЗаполнения` лучше проверок в `ПередЗаписью`?
Она вызывается при проведении и при явном вызове ПроверитьЗаполнение(), работает с флагом «Проверка заполнения» у реквизитов и умеет подсвечивать поле в форме. Простую запись черновика она не блокирует, а ПередЗаписью — блокирует.
Срабатывают ли обработчики объекта при записи через запрос или регистр напрямую?
Запрос в 1С только читает данные, записывать им нельзя. Обработчики модуля объекта срабатывают при вызове Записать(), Удалить() и при записи из формы. У наборов записей регистров свои события — в модуле набора записей.
Как временно отключить обработчик, не удаляя код?
Для подписки — снимите источники или уберите её в расширении. Для модуля объекта — флаг в ДополнительныеСвойства, который проверяется в начале процедуры. Закомментировать код в рабочей базе можно, но это потребует обновления конфигурации.
Почему обработчик ожидания срабатывает, хотя форма закрыта?
Обработчик, подключённый через глобальный контекст, например из модуля управляемого приложения или общего клиентского модуля, не привязан к форме. Отключайте его явно через ОтключитьОбработчикОжидания.