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