При разработке в среде 1С:Предприятие (особенно в конфигурациях на обычных формах, таких как УПП 1.3, КА 1.1) разработчики часто сталкиваются с ситуацией: в модуле формы объявлена процедура-обработчик ПередЗаписью, однако при программной записи документа или справочника из кода этой же формы данное событие попросту игнорируется платформой. Отладчик не заходит в процедуру, а логика, заложенная в форму, не отрабатывает.
В этой статье мы подробно проанализируем архитектурные причины такого поведения платформы 1С, рассмотрим различия для обычных и управляемых форм, а также разберем по шагам готовые решения этой проблемы.
Для того чтобы понять, почему событие не срабатывает, нам необходимо разграничить два понятия: Объект базы данных (например, ДокументОбъект) и Форма (визуальное представление этого объекта на клиенте).
Запись данных может происходить в двух разных контекстах:
ПередЗаписью, ПриЗаписи, и только затем передает управление в модуль самого объекта.Записать() самого объекта. При этом платформа работает напрямую с базой данных, полностью игнорируя интерфейсный слой (форму). Соответственно, события формы физически не могут быть вызваны, так как транзакция записи происходит вне контекста отображения.Выясним причину, почему это происходит в различных типах форм, и как правильно написать код.
В конфигурации УПП 1.3 большинство форм являются обычными. В модуле обычной формы вызов метода Записать() без явного указания контекста интерпретируется системой как обращение к самому прикладному объекту (то есть происходит неявный вызов ЭтотОбъект.Записать()).
В результате данные уходят в базу данных напрямую. Вся логика расширения формы обходится стороной.
Чтобы сымитировать интерактивное нажатие кнопки сохранения пользователем и запустить всю цепочку событий формы (включая проверку заполнения и событие формы ПередЗаписью), необходимо использовать специальный метод расширения формы — ЗаписатьВФорме().
Проанализируем пример некорректного кода:
// НЕПРАВИЛЬНО: события формы ПередЗаписью будут проигнорированы
Процедура ДействиеПриНажатииКнопки(Кнопка)
// Изменяем реквизиты
ЭтотОбъект.Комментарий = "Изменено программно";
// Записываем напрямую объект
ЭтотОбъект.Записать();
КонецПроцедуры
Рассмотрим правильный вариант вызова:
// ПРАВИЛЬНО: сработает вся цепочка событий формы
Процедура ДействиеПриНажатииКнопки(Кнопка)
// Изменяем реквизиты
ЭтотОбъект.Комментарий = "Изменено программно";
// Вызываем метод формы для записи
ЭтаФорма.ЗаписатьВФорме();
КонецПроцедуры
Использование метода ЗаписатьВФорме() гарантирует, что платформа отработает событие ПередЗаписью модуля формы, и если в нем будет установлен параметр Отказ = Истина, запись объекта в базу данных будет успешно заблокирована.
В управляемом приложении архитектура взаимодействия формы и объекта еще более строгая. Часто разработчики совершают ошибку при работе на сервере управляемой формы. Они получают прикладной объект из реквизита формы, вносят изменения и записывают его:
// НЕПРАВИЛЬНО: Запись на сервере вне контекста формы
&НаСервере
Процедура ЗаписатьОбъектНаСервере()
// Конвертируем данные формы в физический объект
ДокОбъект = РеквизитФормыВЗначение("Объект");
ДокОбъект.Комментарий = "Обновлено на сервере";
// Запись происходит на уровне СУБД, форма об этом не знает
ДокОбъект.Записать();
// Данные в форме на клиенте могут стать неактуальными
ЗначениеВРеквизитФормы(ДокОбъект, "Объект");
КонецПроцедуры
При таком подходе клиентские события формы не вызываются принципиально, так как сервер выполняет запись объекта изолированно от интерфейса.
Для корректной программной записи в управляемых формах следует вызывать метод формы Записать() на клиенте. Разберем по шагам правильную схему:
// ПРАВИЛЬНО: инициируем запись через клиентский контекст формы
&НаКлиенте
Процедура ВыполнитьСохранение()
// Параметры записи формы (необязательно)
ПараметрыЗаписи = Новый Структура;
ПараметрыЗаписи.Вставить("РежимЗаписи", РежимЗаписиДокумента.Запись);
// Вызов метода Записать() у самой формы
РезультатЗаписи = ЭтаФорма.Записать(ПараметрыЗаписи);
КонецПроцедуры
При вызове метода ЭтаФорма.Записать() платформа автоматически запустит событие ПередЗаписью на клиенте, затем ПередЗаписьюНаСервере на сервере формы, и только после этого перейдет к транзакции записи самого объекта.
Если вы используете правильные методы записи, но отладчик все равно не заходит в процедуру ПередЗаписью, велика вероятность механической ошибки — отсутствует связь между событием формы и процедурой в коде.
Такое часто случается при переносе кода копированием или при обновлении конфигурации. Выполните следующие действия для проверки:
ПередЗаписью (или Перед записью для обычных форм) указано имя вашей процедуры.Если код написан верно, связь установлена, но событие все равно "пролетает" мимо, воспользуйтесь отличным инструментом платформы — Замером производительности. Он позволяет увидеть весь реальный стек вызовов и понять, где именно прерывается выполнение алгоритма.
Разберем, как им воспользоваться:
Отказ = Истина в модуле объекта) выполнение прекратилось.В сообщении 8 на форуме прозвучала важная архитектурная рекомендация: перенести критически важный функционал в модуль объекта.
Если проверка или заполнение данных критичны для целостности базы данных, никогда не оставляйте их исключительно в модуле формы. Любая обработка, обмен данными или программный запуск со стороны других документов обойдет форму стороной.
Все жесткие бизнес-правила и проверки контроля данных должны располагаться в процедуре ПередЗаписью() модуля объекта. В модуле формы следует оставлять только интерактивные функции (например, вопросы пользователю "Вы уверены?", предупреждения, работу с визуальным оформлением элементов формы).