В процессе сопровождения конфигураций «1С:Зарплата и управление персоналом» или «1С:ERP» часто возникает задача защиты критически важных данных от случайного изменения. Одним из таких случаев является необходимость запретить редактирование параметров расчета страховых взносов (например, поле ВзносыЗаЗанятыхНаРаботахСДосрочнойПенсией) в справочнике ДолжностиОрганизаций, если за этой должностью уже закреплены активные сотрудники. Рассмотрим подробное решение этой задачи, проанализируем возможные подходы и выберем наиболее оптимальный и безопасный вариант реализации.
Нам необходимо заблокировать поле на форме элемента справочника, чтобы пользователь не мог изменить настройки взносов «задним числом», так как это может привести к неверному расчету налогов и необходимости пересчета прошлых периодов. Для реализации этого функционала мы разберем два уровня защиты:
Для начала нам нужно создать эффективный механизм, который ответит на вопрос: «Работает ли кто-то на этой должности?». Рассмотрим, как правильно написать запрос. В обсуждении на форуме предлагалось обращаться к справочнику Сотрудники, однако более профессиональным подходом в современных конфигурациях будет обращение к кадровым регистрам или использование оптимизированного запроса к справочнику сотрудников с учетом особенностей платформы 8.3.
Проанализируем оптимальный код функции, которую следует разместить в модуле менеджера справочника или в привилегированном общем модуле:
Функция ДолжностьЗанятаСотрудниками(СсылкаНаДолжность) Экспорт
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| 1 КАК Поле1
|ИЗ
| Справочник.Сотрудники КАК Сотрудники
|ГДЕ
| Сотрудники.Должность = &Должность
| И НЕ Сотрудники.В Архиве"; // Учитываем только активных сотрудников
Запрос.УстановитьПараметр("Должность", СсылкаНаДолжность);
Возврат НЕ Запрос.Выполнить().Пустой();
КонецФункции
Почему мы используем именно такой код?
ВЫБРАТЬ ПЕРВЫЕ 1 позволяет системе прекратить поиск сразу после нахождения первой же записи, что значительно экономит ресурсы сервера.Пустой() работает быстрее, чем создание выборки и подсчет количества записей через Количество().ВАрхиве позволяет игнорировать уволенных сотрудников, если логика вашей компании допускает изменение настроек должности для новых приемов.Теперь перейдем к интерфейсной части. Нам нужно вызвать нашу проверку при открытии формы и соответствующим образом настроить элементы. Разберем, в каком обработчике это лучше сделать. Чаще всего используют ПриОткрытии, но для управляемых форм правильнее использовать ПриЧтенииНаСервере, так как этот обработчик вызывается и при открытии, и при обновлении данных формы.
В модуле формы элемента пропишем следующий код:
&НаСервере
Процедура ПриЧтенииНаСервере(ТекущийОбъект)
// Вызываем нашу функцию проверки
ЭтоНовыйОбъект = ТекущийОбъект.Ссылка.Пустая();
Если НЕ ЭтоНовыйОбъект Тогда
Занята = Справочники.Должности.ДолжностьЗанятаСотрудниками(ТекущийОбъект.Ссылка);
// Управляем доступностью поля
// Вместо Доступность лучше использовать ТолькоПросмотр
Элементы.ВзносыЗаЗанятыхНаРаботах.ТолькоПросмотр = Занята;
// Можно также вывести информационное сообщение пользователю
Если Занята Тогда
ОбщегоНазначенияКлиентСервер.СообщитьПользователю(
"Редактирование взносов ограничено, так как на должности есть активные сотрудники");
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Разберем разницу между Доступность = Ложь и ТолькоПросмотр = Истина. В первом случае поле становится серым и его содержимое трудно прочитать. Во втором случае текст остается контрастным, его можно выделить и скопировать, но изменить нельзя. Для пользователя второй вариант предпочтительнее.
Важный нюанс, который прозвучал в сообщениях форума — это ограничения RLS (Record Level Security). Если у пользователя, открывающего справочник должностей, нет прав на просмотр списка сотрудников определенных подразделений, обычный запрос вернет «пусто», даже если должность занята. В результате поле станет доступным, что является ошибкой безопасности.
Для решения этой проблемы функцию проверки необходимо выполнять в привилегированном режиме. Это позволит запросу игнорировать права доступа текущего пользователя и проверить реальное наличие записей в базе данных.
Функция ДолжностьЗанятаПривилегированно(СсылкаНаДолжность) Экспорт
УстановитьПривилегированныйРежим(Истина);
Запрос = Новый Запрос("ВЫБРАТЬ ПЕРВЫЕ 1 1 ИЗ Справочник.Сотрудники ГДЕ Должность = &Должность");
Запрос.УстановитьПараметр("Должность", СсылкаНаДолжность);
Возврат НЕ Запрос.Выполнить().Пустой();
КонецФункции
Чтобы гарантировать, что данные не будут изменены «в обход» формы, добавим проверку в модуль объекта. Рассмотрим ситуацию: программист запускает обработку «Групповое изменение реквизитов». Форма в этом случае не открывается, и наши визуальные ограничения не сработают. Чтобы этого избежать, используем событие ПередЗаписью.
Процедура ПередЗаписью(Отказ)
Если ОбменДанными.Загрузка Тогда
Возврат;
КонецЕсли;
// Проверяем, изменился ли критический реквизит
Если НЕ ЭтоНовый() И ЗначениеЗаполнено(Ссылка) Тогда
// Сравниваем текущее значение в базе и то, что пытается записать пользователь
СтароеЗначение = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Ссылка, "ВзносыЗаЗанятыхНаРаботах");
Если СтароеЗначение <> ВзносыЗаЗанятыхНаРаботах Тогда
// Реквизит пытаются изменить, проверяем занятость
Если ДолжностьЗанятаПривилегированно(Ссылка) Тогда
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "Нельзя изменить настройки взносов для занятой должности!";
Сообщение.УстановитьДанные(ЭтотОбъект);
Сообщение.Сообщить();
Отказ = Истина; // Прерываем запись
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Мы проанализировали комплексный подход к защите данных в 1С. Реализация задачи только через форму — это лишь верхушка айсберга. Для создания надежной системы мы объединили несколько техник:
ТолькоПросмотр на управляемой форме.Пустой().ПривилегированныйРежим (обход RLS).Отказ = Истина.Именно такое сочетание методов позволяет гарантировать стабильность учета и отсутствие ошибок при расчете взносов в крупных организациях с большим штатом сотрудников.