При настройке прав доступа и бизнес-процессов часто возникает задача: ограничить возможность отмены проведения старых документов для определенных групп сотрудников (например, экономистов или операторов), сохранив при этом возможность корректировать недавние документы за текущий и предшествующий месяцы. Разберем, как правильно реализовать такое ограничение с помощью программного кода, какие подводные камни могут встретиться в логических условиях и как защитить базу данных от случайных или намеренных обходов запрета.
Рассмотрим типовую ситуацию. Разработчик создает подписку на событие ПередЗаписью документа и пытается отсечь интервал с помощью проверки даты. В исходном варианте код часто выглядит следующим образом:
Процедура ПередЗаписью(Источник, Отказ, РежимЗаписи, РежимПроведения) Экспорт
Если (РежимЗаписи = РежимЗаписиДокумента.ОтменаПроведения ИЛИ РежимЗаписи = РежимЗаписиДокумента.Проведение ИЛИ Источник.Проведен)
И НачалоМесяца(ТекущаяДата) <> НачалоМесяца(ТекущаяДата)
И РольДоступна("ДляЭкономистов") Тогда
Отказ = Истина;
Сообщить("нельзя!", СтатусСообщения.Важное);
КонецЕсли;
КонецПроцедуры
Проанализируем, почему этот код не выполняет проверку интервала. Выражение НачалоМесяца(ТекущаяДата) <> НачалоМесяца(ТекущаяДата) сравнивает одно и то же значение само с собой. Результат такого сравнения всегда будет равен Ложь, а значит, всё составное условие никогда не выполнится, и отмена проведения не будет заблокирована.
Кроме того, чтобы проверить ограничение на два месяца назад от текущей даты, необходимо обращаться к дате самого документа (Источник.Дата) и сравнивать ее с расчетной датой границы запрета.
Для смещения даты на определенное количество месяцев в платформе 1С предусмотрена функция ДобавитьМесяц(). Если требуется запретить отмену проведения документов, которые были оформлены ранее чем за последние два месяца, условие должно проверять, что дата документа строго меньше вычисленной границы.
Посмотрим на исправленное условие проверки:
Если Источник.Дата < ДобавитьМесяц(ТекущаяДатаСеанса(), -2) Тогда
// Документ старше двух месяцев
КонецЕсли;
Обратим внимание на знак сравнения: если указать Источник.Дата > ДобавитьМесяц(...), блокировка сработает наоборот — система заблокирует проведение и отмену проведения новых документов, но разрешит менять старые. Поэтому используем именно оператор «меньше» (<).
Если ограничиться только сравнением Источник.Дата в подписке на событие, в системе остаются логические лазейки. Рассмотрим основные нюансы, о которых важно помнить при разработке надежного механизма:
Источник.Дата станет текущей, условие не сработает, и документ успешно снимется с проведения. Чтобы закрыть эту уязвимость, необходимо проверять не только новую дату, но и исходную дату объекта в базе данных через Источник.Ссылка.Дата.ТекущаяДата() возвращает локальное время операционной системы сервера 1С. По стандартам разработки платформы 1С рекомендуется применять функцию ТекущаяДатаСеанса(), которая учитывает часовой пояс сеанса конкретного пользователя.ДобавитьМесяц() сохраняет точное время. Если требуется запретить документы строго по границе календарных месяцев (например, весь период до начала месяца, отстоящего на два назад), результат функции обязательно следует обернуть в НачалоМесяца().РежимЗаписиДокумента.ОтменаПроведения, но и при записи в режиме РежимЗаписиДокумента.Запись, если до этого он был проведен. Надежная проверка факта отмены проведения должна учитывать: (РежимЗаписи = РежимЗаписиДокумента.ОтменаПроведения) ИЛИ (Источник.Ссылка.Проведен И НЕ Источник.Проведен).РольДоступна("ДляЭкономистов") вернет Истина и для администратора, если эта роль включена в его профиль. Чтобы не заблокировать работу администраторов и регламентных заданий, добавим проверку на привилегированный режим и полные права.Соберем все требования воедино и посмотрим на надежный вариант реализации обработчика подписки на событие ПередЗаписью:
Процедура ЗапретОтменыПроведенияДокументовПередЗаписью(Источник, Отказ, РежимЗаписи, РежимПроведения) Экспорт
// Проверяем, выполняется ли код в привилегированном режиме
Если ПривилегированныйРежим() Тогда
Возврат;
КонецЕсли;
// Если у пользователя есть полные права, ограничение не накладываем
Если РольДоступна("ПолныеПрава") Тогда
Возврат;
КонецЕсли;
// Проверяем роль целевого пользователя
Если НЕ РольДоступна("ДляЭкономистов") Тогда
Возврат;
КонецЕсли;
// Определяем, происходит ли отмена проведения (явно или сбросом флага)
СнятиеСПроведения = (РежимЗаписи = РежимЗаписиДокумента.ОтменаПроведения)
ИЛИ (Источник.Ссылка.Проведен И НЕ Источник.Проведен);
Если НЕ СнятиеСПроведения Тогда
Возврат;
КонецЕсли;
// Рассчитываем граничную дату: начало месяца два месяца назад
МинимальнаяДата = НачалоМесяца(ДобавитьМесяц(ТекущаяДатаСеанса(), -2));
// Проверяем как текущую дату в форме, так и дату, сохраненную в базе данных
Если (Источник.Дата < МинимальнаяДата) ИЛИ (Источник.Ссылка.Дата < МинимальнаяДата) Тогда
Отказ = Истина;
ВызватьИсключение "Отмена проведения документов за период ранее "
+ Формат(МинимальнаяДата, "ДФ=dd.MM.yyyy") + " запрещена!";
КонецЕсли;
КонецПроцедуры
Помимо подписки на событие ПередЗаписью, в платформе 1С существуют другие архитектурные подходы для решения подобной задачи:
ОбработкаУдаленияПроведения: У объектов документов есть отдельное событие, предназначенное исключительно для обработки отмены проведения. Использование этого события позволяет отказаться от проверки флагов и режимов записи, так как процедура вызывается только при снятии документа с проведения.ДляЭкономистов, сняв флаг «Интерактивная отмена проведения». В этом случае пользователь вообще не увидит в контекстном меню и командной панели кнопку отмены проведения, что исключит необходимость написания программных проверок.