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