Как в подписке на событие ПередЗаписью отследить нажатие кнопки ОК и предотвратить закрытие формы?

Программист 1С v8.3 (Обычные формы) Управленческий учет IT и автоматизация бизнеса
← На главную

При доработке типовых конфигураций разработчики часто стремятся минимизировать вмешательство в типовые формы и модули, используя подписки на события. Рассмотрим практическую ситуацию: при создании нового элемента справочника Номенклатура требуется отловить нажатие именно кнопки ОК, чтобы заблокировать закрытие формы, оставив при этом возможность сохранения по кнопке Записать (или Сохранить). Выясним, возможно ли это сделать через механизм подписок, и разберем корректные способы решения сопутствующих задач производительности.

Архитектурные ограничения подписок на события объекта

Проанализируем работу событийной модели платформы «1С:Предприятие». В платформе четко разделены два уровня:

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

Посмотрим на сигнатуру стандартного обработчика подписки ПередЗаписью для справочника:


Процедура ПодпискаПередЗаписьюНоменклатуры(Источник, Отказ) Экспорт
    // Источник - СправочникОбъект.Номенклатура
    // Отказ    - Булево
КонецПроцедуры

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

Если мы установим Отказ = Истина внутри подписки, платформа отменит всю транзакцию записи целиком — данные не сохранятся в базе вообще, а форма выдаст ошибку записи. Управлять закрытием формы из подписки уровня объекта невозможно.

Анализ исходной проблемы: зависание при смене коэффициентов

Разберем первопричину, из-за которой возникает потребность запретить закрытие формы. В конфигурациях на обычных формах (например, УПП 1.3 или УТ 10.3) при повторном открытии созданной номенклатуры и редактировании подчиненного справочника ЕдиницыИзмерения (изменение реквизита Коэффициент) система запускает проверку ссылочной целостности.

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

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

Решение 1. Перехват события на уровне формы

Если необходимо скорректировать именно поведение формы (не позволять закрывать окно по кнопке ОК для определенных групп номенклатуры), такую логику следует выносить в модуль самой формы.

Рассмотрим переопределение действия при закрытии формы в процедуре ПередЗакрытием:


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

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

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

Решение 2. Оптимизация типовой проверки использования единиц

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

  1. Проверка даты создания или наличия движений: если номенклатура создана недавно (например, в текущий день) и по ней заведомо нет закрытых периодов, можно ограничить поиск только оперативными остатками.
  2. Использование быстрых точечных запросов: вместо полного перебора всех таблиц метаданных проверяем только ключевые регистры с конструкцией ограничения выборки:

Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ ПЕРВЫЕ 1
    |	ТоварыНаСкладах.Номенклатура КАК Номенклатура
    |ИЗ
    |	РегистрНакопления.ТоварыНаСкладах КАК ТоварыНаСкладах
    |ГДЕ
    |	ТоварыНаСкладах.Номенклатура = &Номенклатура
    |	И ТоварыНаСкладах.ЕдиницаИзмерения = &ЕдиницаИзмерения";

Запрос.УстановитьПараметр("Номенклатура", СсылкаНаНоменклатуру);
Запрос.УстановитьПараметр("ЕдиницаИзмерения", СсылкаНаЕдиницу);

Результат = Запрос.Выполнить();
Если Не Результат.Пустой() Тогда
    ВызватьИсключение "Нельзя изменить коэффициент: по единице измерения уже есть движения!";
КонецЕсли;

Конструкция ВЫБРАТЬ ПЕРВЫЕ 1 по индексированным полям отрабатывает за доли секунды даже на таблицах в десятки миллионов записей.

Решение 3. Автоматическое формирование единиц измерения при записи

Чтобы исключить человеческий фактор и необходимость ручной правки единиц измерения после сохранения, применим подписку на событие ПриЗаписи или ПередЗаписью по прямому назначению — для автоматической подготовки связанных данных.

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


Процедура ПриЗаписиНоменклатурыАвтоЕдиницы(Источник, Отказ) Экспорт
    
    // Проверяем, что объект новый и входит в нужную группу
    Если Источник.ДополнительныеСвойства.Свойство("ЭтоНовыйЭлемент") Тогда
        // Создаем необходимые подчиненные единицы программно
        СоздатьПодчиненныеЕдиницыПоШаблону(Источник.Ссылка, Источник.БазоваяЕдиницаИзмерения);
    КонецЕсли;

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

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

← На главную