Как реализовать автоматическое обновление статусов стажеров в документе 1С

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

В процессе разработки кадрового учета часто возникает задача автоматизации жизненного цикла сотрудника. Например, нам необходимо, чтобы система самостоятельно меняла статус в документе КП_ДокументыПоСтажерам в зависимости от даты трудоустройства или наличия оценки руководителя. Рассмотрим, как правильно реализовать такую логику, избежав типичных ошибок начинающих разработчиков, таких как избыточная запись объектов и неверная последовательность условий.

Разбор бизнес-логики и постановка задачи

Для корректной работы алгоритма мы должны четко определить приоритеты статусов. В нашем примере действуют следующие правила:

  1. Если в базе уже существует элемент справочника КП_ОценкаСотрудника для данного стажера, статус должен безусловно принимать значение Прошел испытательный срок.
  2. Если оценки еще нет, но наступила или прошла ДатаТрудоустройства, статус меняется на На испытательном сроке.
  3. Если дата трудоустройства еще в будущем, устанавливается статус Планируется выход.

Анализ исходного кода и поиск ошибок

Проанализируем первоначальный подход. В исходном коде использовались идущие подряд блоки Если...КонецЕсли. Это приводило к тому, что одно условие могло перезаписывать результат другого. Также критической ошибкой является выполнение запросов внутри цикла — это многократно замедляет работу системы. Кроме того, вызов метода Записать() без проверки того, изменился ли статус на самом деле, создает ненужную нагрузку на базу данных и блокировки таблиц.

Оптимизированное решение с использованием структуры ИначеЕсли

Разберем правильный подход к организации цикла обработки документов. Вместо множества независимых условий мы используем единую конструкцию Если...ИначеЕсли. Это гарантирует, что для одного документа сработает только одна ветка логики.


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

Работа с датами и исключение ошибок сравнения

Обратим внимание на использование функции НачалоДня(). В 1С тип Дата всегда содержит время. Если мы сравним "01.10.2023 00:00:00" (дата из документа) и "01.10.2023 15:40:00" (текущее время), оператор "меньше" вернет Истина, хотя календарный день еще не закончился. Чтобы статус На испытательном сроке устанавливался корректно именно в день выхода, всегда приводите обе даты к началу дня.

Оптимизация проверки связанных данных

Вспомогательная функция ПроверитьНаличиеЛистаОценки должна возвращать булево значение. Однако помните, что поиск по строковому полю (ФИО) является ненадежным. Рекомендуется использовать ссылки на справочник ФизическиеЛица.


Функция ПроверитьНаличиеЛистаОценки(СотрудникСсылка)
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
    "ВЫБРАТЬ ПЕРВЫЕ 1
    |    Оценка.Ссылка
    |ИЗ
    |    Справочник.КП_ОценкаСотрудника КАК Оценка
    |ГДЕ
    |    Оценка.Сотрудник = &Сотрудник";
    
    Запрос.УстановитьПараметр("Сотрудник", СотрудникСсылка);
    
    Возврат НЕ Запрос.Выполнить().Пустой();
    
КонецФункции

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

Рассмотрим, как профессиональный разработчик оптимизировал бы этот код. Вместо того чтобы для каждого стажера вызывать функцию с отдельным запросом, мы можем сразу в основном запросе соединить документы с таблицей оценок. Это называется Левое соединение. Если оценка есть — поле в результате запроса будет заполнено, если нет — будет NULL.

Проанализируем фрагмент такого запроса:


ВЫБРАТЬ
    Док.Ссылка КАК Ссылка,
    Док.СтатусСтажера КАК СтатусСтажера,
    Док.ДатаТрудоустройства КАК ДатаТрудоустройства,
    ЕСТЬNULL(Оценка.Ссылка, ЗНАЧЕНИЕ(Справочник.КП_ОценкаСотрудника.ПустаяСсылка)) КАК ЛистОценки
ИЗ
    Документ.КП_ДокументыПоСтажерам КАК Док
    ЛЕВОЕ СОЕДИНЕНИЕ Справочник.КП_ОценкаСотрудника КАК Оценка
    ПО Док.ФИО = Оценка.Сотрудник
ГДЕ
    Док.Проведен

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

Безопасность при работе в регламентных заданиях

Если данный код планируется запускать через РегламентныеЗадания, необходимо учитывать, что они выполняются на сервере. В этом случае нельзя использовать методы типа Предупреждение() или Вопрос(). Также следует оборачивать запись объекта в блок Попытка...Исключение, так как документ может быть заблокирован пользователем, открывшим его в этот момент в интерфейсе. Использование ЗаписьЖурналаРегистрации поможет вам отследить такие ситуации без прерывания работы всего алгоритма.

Резюме

Для надежной работы автоматической смены статусов мы применили:

  1. Строгую иерархию условий через ИначеЕсли, исключающую двойную трактовку статуса.
  2. Сравнение дат через НачалоДня для точности в пределах суток.
  3. Проверку модифицированности перед вызовом Записать(), что экономит ресурсы сервера.
  4. Журналирование ошибок для контроля работы фоновых процессов.

Следуя этим правилам, вы создадите стабильный и производительный механизм управления состояниями объектов в вашей конфигурации 1С.

← На главную