gifts2017

Готовое решение для отслеживания конфликтов кадровых неявок в ЗУП и УПП

Опубликовал megatrend - (megatrend) в раздел Администрирование - Поиск данных

В актуальной версии ЗУП 2.5 оперативный контроль кадровых неявок сводится к программной проверке : не начинается ли в эту же самую дату какая-то другая неявка? Многие пользователи считают, что этого явно недостаточно.

Вашему вниманию предлагается готовое решение (в виде программного кода) для ЗУП и УПП, которое после проведения документа кадровой неявки информирует пользователя, какие конфликты кадровых неявок повлекло за собой проведение документа.

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

Сейчас я приведу конкретный пример, как этот запрос можно использовать в типовой конфигурации ЗУП (УПП) для информирования пользователя о конфликтах кадровых неявок при проведении любого кадрового документа, регистрирующего кадровые неявки.

Для этого в дереве конфигурации нужно создать новую подписку на событие, скажем, ОперативныйКонтрольНеявок :

Источник события = РегистрСведенийНаборЗаписей.СостояниеРаботниковОрганизаций,
Событие = ПриЗаписи,
а в качестве процедуры - обработчика события использовать следующий код :

 

Процедура ОперативныйКонтрольНеявокПриЗаписи(Источник, Отказ, Замещение) Экспорт

   
// в наборе нет записей - отслеживаемые неявки отсутствуют - контроль не нужен
   
Если Источник.Количество() = 0 Тогда
        Возврат;
    КонецЕсли;

   
// у всего набора записей регистратор - один и тот же
   
Регистратор1 = Источник[0].Регистратор;

   
Запрос = Новый Запрос;
   
Запрос.Текст = "ВЫБРАТЬ
    |   СостояниеРаботниковОрганизаций.Период КАК Период,
    |   СостояниеРаботниковОрганизаций.Регистратор,
    |   СостояниеРаботниковОрганизаций.НомерСтроки,
    |   СостояниеРаботниковОрганизаций.Активность,
    |   СостояниеРаботниковОрганизаций.Сотрудник,
    |   СостояниеРаботниковОрганизаций.Организация,
    |   СостояниеРаботниковОрганизаций.Состояние,
    |   СостояниеРаботниковОрганизаций.ПериодЗавершения,
    |   СостояниеРаботниковОрганизаций.СостояниеЗавершения
    |ПОМЕСТИТЬ ДвиженияОтслеживаемогоРегистратора
    |ИЗ
    |   РегистрСведений.СостояниеРаботниковОрганизаций КАК СостояниеРаботниковОрганизаций
    |ГДЕ
    |   СостояниеРаботниковОрганизаций.Регистратор = &ОтслеживаемыйРегистратор
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ РАЗЛИЧНЫЕ
    |   ДвиженияОтслеживаемогоРегистратора.Сотрудник
    |ПОМЕСТИТЬ ОптимизацияОтслеживаемыеСотрудники
    |ИЗ
    |   ДвиженияОтслеживаемогоРегистратора КАК ДвиженияОтслеживаемогоРегистратора
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |   МИНИМУМ(ДвиженияОтслеживаемогоРегистратора.Период) КАК Период
    |ПОМЕСТИТЬ ОптимизацияМинимальныеИнтересующиеПериодыКалендаря
    |ИЗ
    |   ДвиженияОтслеживаемогоРегистратора КАК ДвиженияОтслеживаемогоРегистратора
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |   РегламентированныйПроизводственныйКалендарь.ДатаКалендаря
    |ПОМЕСТИТЬ ОптимизацияКалендарь
    |ИЗ
    |   РегистрСведений.РегламентированныйПроизводственныйКалендарь КАК РегламентированныйПроизводственныйКалендарь
    |       ВНУТРЕННЕЕ СОЕДИНЕНИЕ ОптимизацияМинимальныеИнтересующиеПериодыКалендаря КАК ОптимизацияМинимальныеИнтересующиеПериодыКалендаря
    |       ПО РегламентированныйПроизводственныйКалендарь.ДатаКалендаря >= ОптимизацияМинимальныеИнтересующиеПериодыКалендаря.Период
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |   СостояниеРаботниковОрганизаций.Период КАК Период,
    |   СостояниеРаботниковОрганизаций.Регистратор,
    |   СостояниеРаботниковОрганизаций.НомерСтроки,
    |   СостояниеРаботниковОрганизаций.Активность,
    |   СостояниеРаботниковОрганизаций.Сотрудник,
    |   СостояниеРаботниковОрганизаций.Организация,
    |   СостояниеРаботниковОрганизаций.Состояние,
    |   СостояниеРаботниковОрганизаций.ПериодЗавершения,
    |   СостояниеРаботниковОрганизаций.СостояниеЗавершения
    |ПОМЕСТИТЬ ДвиженияБезОтслеживаемогоРегистратора
    |ИЗ
    |   РегистрСведений.СостояниеРаботниковОрганизаций КАК СостояниеРаботниковОрганизаций
    |ГДЕ
    |   СостояниеРаботниковОрганизаций.Регистратор <> &ОтслеживаемыйРегистратор
    |   И СостояниеРаботниковОрганизаций.Сотрудник В
    |           (ВЫБРАТЬ
    |               ОптимизацияОтслеживаемыеСотрудники.Сотрудник
    |           ИЗ
    |               ОптимизацияОтслеживаемыеСотрудники)
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |   МАКСИМУМ(ВЫБОР
    |           КОГДА ДвиженияБезОтслеживаемогоРегистратора.ПериодЗавершения = ДАТАВРЕМЯ(1, 1, 1, 0, 0, 0)
    |               ТОГДА ДвиженияБезОтслеживаемогоРегистратора.Период
    |           КОГДА ДвиженияБезОтслеживаемогоРегистратора.ПериодЗавершения > РегламентированныйПроизводственныйКалендарь.ДатаКалендаря
    |               ТОГДА ДвиженияБезОтслеживаемогоРегистратора.Период
    |           ИНАЧЕ ДвиженияБезОтслеживаемогоРегистратора.ПериодЗавершения
    |       КОНЕЦ) КАК Период,
    |   РегламентированныйПроизводственныйКалендарь.ДатаКалендаря,
    |   ДвиженияБезОтслеживаемогоРегистратора.Сотрудник
    |ПОМЕСТИТЬ ПериодыСостоянийСотрудниковБезОтслеживаемогоРегистратора
    |ИЗ
    |   ДвиженияБезОтслеживаемогоРегистратора КАК ДвиженияБезОтслеживаемогоРегистратора
    |       ВНУТРЕННЕЕ СОЕДИНЕНИЕ ОптимизацияКалендарь КАК РегламентированныйПроизводственныйКалендарь
    |       ПО ДвиженияБезОтслеживаемогоРегистратора.Период <= РегламентированныйПроизводственныйКалендарь.ДатаКалендаря
    |
    |СГРУППИРОВАТЬ ПО
    |   РегламентированныйПроизводственныйКалендарь.ДатаКалендаря,
    |   ДвиженияБезОтслеживаемогоРегистратора.Сотрудник
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |   МАКСИМУМ(ВЫБОР
    |           КОГДА ДвиженияОтслеживаемогоРегистратора.ПериодЗавершения = ДАТАВРЕМЯ(1, 1, 1, 0, 0, 0)
    |               ТОГДА ДвиженияОтслеживаемогоРегистратора.Период
    |           КОГДА ДвиженияОтслеживаемогоРегистратора.ПериодЗавершения > РегламентированныйПроизводственныйКалендарь.ДатаКалендаря
    |               ТОГДА ДвиженияОтслеживаемогоРегистратора.Период
    |           ИНАЧЕ ДвиженияОтслеживаемогоРегистратора.ПериодЗавершения
    |       КОНЕЦ) КАК Период,
    |   РегламентированныйПроизводственныйКалендарь.ДатаКалендаря,
    |   ДвиженияОтслеживаемогоРегистратора.Сотрудник
    |ПОМЕСТИТЬ ПериодыСостоянийСотрудниковОтслеживаемогоРегистратора
    |ИЗ
    |   ДвиженияОтслеживаемогоРегистратора КАК ДвиженияОтслеживаемогоРегистратора
    |       ВНУТРЕННЕЕ СОЕДИНЕНИЕ ОптимизацияКалендарь КАК РегламентированныйПроизводственныйКалендарь
    |       ПО ДвиженияОтслеживаемогоРегистратора.Период <= РегламентированныйПроизводственныйКалендарь.ДатаКалендаря
    |
    |СГРУППИРОВАТЬ ПО
    |   РегламентированныйПроизводственныйКалендарь.ДатаКалендаря,
    |   ДвиженияОтслеживаемогоРегистратора.Сотрудник
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |   ПериодыСостоянийСотрудников.ДатаКалендаря КАК ДатаКалендаря,
    |   ПериодыСостоянийСотрудников.Сотрудник,
    |   ДвиженияБезОтслеживаемогоРегистратора.Регистратор,
    |   ВЫБОР
    |       КОГДА ПериодыСостоянийСотрудников.Период = ДвиженияБезОтслеживаемогоРегистратора.Период
    |           ТОГДА ДвиженияБезОтслеживаемогоРегистратора.Состояние
    |       ИНАЧЕ ДвиженияБезОтслеживаемогоРегистратора.СостояниеЗавершения
    |   КОНЕЦ КАК Состояние
    |ПОМЕСТИТЬ КалендарьСостоянийСотрудниковБезОтслеживаемогоРегистратора
    |ИЗ
    |   ПериодыСостоянийСотрудниковБезОтслеживаемогоРегистратора КАК ПериодыСостоянийСотрудников
    |       ВНУТРЕННЕЕ СОЕДИНЕНИЕ ДвиженияБезОтслеживаемогоРегистратора КАК ДвиженияБезОтслеживаемогоРегистратора
    |       ПО (ПериодыСостоянийСотрудников.Период = ДвиженияБезОтслеживаемогоРегистратора.Период
    |               ИЛИ ПериодыСостоянийСотрудников.Период = ДвиженияБезОтслеживаемогоРегистратора.ПериодЗавершения)
    |           И ПериодыСостоянийСотрудников.Сотрудник = ДвиженияБезОтслеживаемогоРегистратора.Сотрудник
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |   ПериодыСостоянийСотрудников.ДатаКалендаря КАК ДатаКалендаря,
    |   ПериодыСостоянийСотрудников.Сотрудник,
    |   ДвиженияОтслеживаемогоРегистратора.Регистратор,
    |   ВЫБОР
    |       КОГДА ПериодыСостоянийСотрудников.Период = ДвиженияОтслеживаемогоРегистратора.Период
    |           ТОГДА ДвиженияОтслеживаемогоРегистратора.Состояние
    |       ИНАЧЕ ДвиженияОтслеживаемогоРегистратора.СостояниеЗавершения
    |   КОНЕЦ КАК Состояние
    |ПОМЕСТИТЬ КалендарьСостоянийСотрудниковОтслеживаемогоРегистратора
    |ИЗ
    |   ПериодыСостоянийСотрудниковОтслеживаемогоРегистратора КАК ПериодыСостоянийСотрудников
    |       ВНУТРЕННЕЕ СОЕДИНЕНИЕ ДвиженияОтслеживаемогоРегистратора КАК ДвиженияОтслеживаемогоРегистратора
    |       ПО (ПериодыСостоянийСотрудников.Период = ДвиженияОтслеживаемогоРегистратора.Период
    |               ИЛИ ПериодыСостоянийСотрудников.Период = ДвиженияОтслеживаемогоРегистратора.ПериодЗавершения)
    |           И ПериодыСостоянийСотрудников.Сотрудник = ДвиженияОтслеживаемогоРегистратора.Сотрудник
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ РАЗЛИЧНЫЕ
    |   КалендарьСостоянийСотрудниковБезОтслеживаемогоРегистратора.ДатаКалендаря,
    |   КалендарьСостоянийСотрудниковБезОтслеживаемогоРегистратора.Сотрудник,
    |   КалендарьСостоянийСотрудниковБезОтслеживаемогоРегистратора.Регистратор,
    |   КалендарьСостоянийСотрудниковБезОтслеживаемогоРегистратора.Состояние
    |ПОМЕСТИТЬ КалендарьНеявокСотрудниковБезОтслеживаемогоРегистратора
    |ИЗ
    |   КалендарьСостоянийСотрудниковБезОтслеживаемогоРегистратора КАК КалендарьСостоянийСотрудниковБезОтслеживаемогоРегистратора
    |ГДЕ
    |   КалендарьСостоянийСотрудниковБезОтслеживаемогоРегистратора.Состояние <> ЗНАЧЕНИЕ(Перечисление.СостоянияРаботникаОрганизации.Работает)
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ РАЗЛИЧНЫЕ
    |   КалендарьСостоянийСотрудниковОтслеживаемогоРегистратора.ДатаКалендаря,
    |   КалендарьСостоянийСотрудниковОтслеживаемогоРегистратора.Сотрудник,
    |   КалендарьСостоянийСотрудниковОтслеживаемогоРегистратора.Регистратор,
    |   КалендарьСостоянийСотрудниковОтслеживаемогоРегистратора.Состояние
    |ПОМЕСТИТЬ КалендарьНеявокСотрудниковОтслеживаемогоРегистратора
    |ИЗ
    |   КалендарьСостоянийСотрудниковОтслеживаемогоРегистратора КАК КалендарьСостоянийСотрудниковОтслеживаемогоРегистратора
    |ГДЕ
    |   КалендарьСостоянийСотрудниковОтслеживаемогоРегистратора.Состояние <> ЗНАЧЕНИЕ(Перечисление.СостоянияРаботникаОрганизации.Работает)
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |   КалендарьНеявокСотрудниковБезОтслеживаемогоРегистратора.ДатаКалендаря КАК ДатаКалендаряМаксимум,
    |   КалендарьНеявокСотрудниковБезОтслеживаемогоРегистратора.Сотрудник КАК Сотрудник,
    |   КалендарьНеявокСотрудниковБезОтслеживаемогоРегистратора.Регистратор КАК Регистратор,
    |   КалендарьНеявокСотрудниковБезОтслеживаемогоРегистратора.Состояние КАК СостояниеБезОтслеживаемогоРегистратора,
    |   КалендарьНеявокСотрудниковОтслеживаемогоРегистратора.Состояние КАК СостояниеОтслеживаемогоРегистратора,
    |   КалендарьНеявокСотрудниковБезОтслеживаемогоРегистратора.ДатаКалендаря КАК ДатаКалендаряМинимум
    |ИЗ
    |   КалендарьНеявокСотрудниковБезОтслеживаемогоРегистратора КАК КалендарьНеявокСотрудниковБезОтслеживаемогоРегистратора
    |       ВНУТРЕННЕЕ СОЕДИНЕНИЕ КалендарьНеявокСотрудниковОтслеживаемогоРегистратора КАК КалендарьНеявокСотрудниковОтслеживаемогоРегистратора
    |       ПО КалендарьНеявокСотрудниковБезОтслеживаемогоРегистратора.Сотрудник = КалендарьНеявокСотрудниковОтслеживаемогоРегистратора.Сотрудник
    |           И КалендарьНеявокСотрудниковБезОтслеживаемогоРегистратора.ДатаКалендаря = КалендарьНеявокСотрудниковОтслеживаемогоРегистратора.ДатаКалендаря
    |           И КалендарьНеявокСотрудниковБезОтслеживаемогоРегистратора.Состояние <> КалендарьНеявокСотрудниковОтслеживаемогоРегистратора.Состояние
    |ИТОГИ
    |   МАКСИМУМ(ДатаКалендаряМаксимум),
    |   МИНИМУМ(ДатаКалендаряМинимум)
    |ПО
    |   Сотрудник,
    |   Регистратор,
    |   СостояниеОтслеживаемогоРегистратора,
    |   СостояниеБезОтслеживаемогоРегистратора"
;

   
Запрос.УстановитьПараметр("ОтслеживаемыйРегистратор", Регистратор1);

   
Результат = Запрос.Выполнить();
   
Выборка = Результат.Выбрать();

    Если
Выборка.Количество() > 0 Тогда

       
ОбработкаКомментариев = глЗначениеПеременной("глОбработкаСообщений");
       
ОбработкаКомментариев.УдалитьСообщения();
       
ТекстСообщения = "Проведение документа " + Регистратор1 + " : ";
       
ЗаголовокСообщения = ОбработкаКомментариев.ДобавитьСообщение(ТекстСообщения, Перечисления.ВидыСообщений.ВажнаяИнформация);

        Пока
Выборка.СледующийПоЗначениюПоля("Сотрудник") Цикл

            Если НЕ
ЗначениеЗаполнено(Выборка.Сотрудник) Тогда
                Продолжить;
            КонецЕсли;

            Пока
Выборка.СледующийПоЗначениюПоля("Регистратор") Цикл

                Если НЕ
ЗначениеЗаполнено(Выборка.Регистратор) Тогда
                    Продолжить;
                КонецЕсли;

                Пока
Выборка.СледующийПоЗначениюПоля("СостояниеОтслеживаемогоРегистратора") Цикл

                    Если НЕ
ЗначениеЗаполнено(Выборка.СостояниеОтслеживаемогоРегистратора) Тогда
                        Продолжить;
                    КонецЕсли;

                    Пока
Выборка.СледующийПоЗначениюПоля("СостояниеБезОтслеживаемогоРегистратора") Цикл

                        Если НЕ
ЗначениеЗаполнено(Выборка.СостояниеБезОтслеживаемогоРегистратора) Тогда
                            Продолжить;
                        КонецЕсли;

                       
ОбработкаКомментариев.ДобавитьСообщение("Сотрудник `" + Выборка.Сотрудник + "' (" + Выборка.СостояниеОтслеживаемогоРегистратора + " " + Формат(Выборка.ДатаКалендаряМинимум, "ДФ=dd.MM.yyyy") + " - " + Формат(Выборка.ДатаКалендаряМаксимум, "ДФ=dd.MM.yyyy") + ") уже переведен в состояние """ + Выборка.СостояниеБезОтслеживаемогоРегистратора + """ документом " + Выборка.Регистратор + ""
                       
, Перечисления.ВидыСообщений.Информация, , ЗаголовокСообщения);

                    КонецЦикла;
                КонецЦикла;
            КонецЦикла;
        КонецЦикла;

       
ОбработкаКомментариев.ПоказатьСообщения();
    КонецЕсли;

КонецПроцедуры

Как это работает?

Данный обработчик "срабатывает" при каждой записи набора записей  в регистр сведений СостояниеРаботниковОрганизаций вне зависимости от вида документа, которому заблагорассудится написать в этот регистр сведений.

Поскольку этот регистр сведений подчинён регистратору, запись (непустого набора) в регистр фактически происходит при проведении какого-либо кадрового документа (уполномоченного на запись в этот регистр).

Если записываемый набор оказывается пустым, то конфликтов неявок быть не может, и обработчик завершает работу. В противном случае набор записей содержит ссылку на документ, который сгенерировал этот набор записей.

Проведение документа и запись в регистр сведений произойдёт только после необходимых проверок, которые выполняет типовой код ЗУП (УПП). Если типовой код ЗУП (УПП) запрещает проведение документа, то запись в регистр СостояниеРаботниковОрганизаций не осуществляется и обработчик события ПриЗаписи не запустится.

Что же произойдёт после того, как типовой код ЗУП (УПП) разрешит проведение кадрового документа? В регистр сведений СостояниеРаботниковОрганизаций будет записан новый набор записей и сразу после этого запустится обработчик события.

Далее возможны 2 варианта :

  1. Если конфликты неявок не будут обнаружены, то подписка на событие отработает, не оставив после себя никаких видимых следов.
  2. Если конфликт неявок будет обнаружен, то документ будет проведён, но пользователю на экран выдастся табличка с сообщением такого вида:

 

Пример оперативного контроля неявок при проведении кадрового документа

 

Интервал конфликтных дат в этом информационном сообщении будет ТОЧНЫМ, то есть - в сообщении будут указаны только те календарные даты, за которые конфликтует проводимый документ со всеми другими проведёнными документами.

Примечание : поскольку никакие виды документов и никакие виды неявок в представленном мной коде явно не поименованы, этот код не нужно будет модифицировать при добавлении «своих» видов документов (например, «Отзыв из отпуска», «Отзыв из командировки») или при добавлении «своих» видов неявок.

См. также

Подписаться Добавить вознаграждение
Комментарии
1. Артур Аюханов (artbear) 30.06.11 09:16
(0) Не хватает условия в начале метода
Если Источник.обменДанными.Загрузка Тогда Возврат; КонецЕсли
2. megatrend - (megatrend) 30.06.11 11:16
(1) это чтобы обработчик не мешал через XML перекачивать документы вместе с движениями? :|
3. MefistO mefistoguru (kravella) 14.07.11 22:54
Спасибо, мне понравилась идея. Уже пользуюсь.
megatrend; +1 Ответить
4. Евгений ЕЕ (zelek) 09.11.11 10:11
Не отрабатывает контроль наложения отпусков организаций.
Например: первый док. отпуск с 01.10.2011 по 31.10.2011, второй док. с 10.10.2011 по 10.10.2011 - контроль не отрабатывает.
5. megatrend - (megatrend) 09.11.11 10:26
(4) zelek, попробуй убрать из запроса строку "И КалендарьНеявокСотрудниковБезОтслеживаемогоРегистратора.Состояние <> КалендарьНеявокСотрудниковОтслеживаемогоРегистратора.Состояние"

PS. Моих кадровиков мой запрос устраивает :)
6. Евгений ЕЕ (zelek) 09.11.11 14:44
Спасибо, помогло.
Запрос устраивает на все 100% :)
megatrend; +1 Ответить
7. Антон Лыткин (Trise) 25.03.13 09:10