Проверка пересечения кадровых неявок, отпусков, командировок

Процедура для подписки на событие для контроля пересечений кадровых неявок. Необходимы изменения в конфигурации.

В работе кадрового отдела часто возникают ситуации, когда пересекаются периоды отпусков, командировок или болезней.
Например, больничный сотрудника 11.05 — 20.05. Но вводится кадровый отпуск отпуск с 18.05.
При этом рассчетчики, формируя документы через АнализНеявок, получают неверные данные: больничный создается с 11.05 по 17.05.
Чтобы избежать подобных проблем, предлагаю контроль пересечений кадровых отклонений, найденные пересечения сообщаются пользователю. Реализовано это подпиской на события «Обработка проведения» документов «ОтпускаОрганизаций», «НеявкиИБолезниОрганизаций», и «КомандировкиОрганизаций».

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

Процедура ОбработкаПроведенияКонтрольПересеченияНеявкиИБолезни(Источник,Отказ, Режим)   Экспорт

Если ТипЗнч(Источник) = тип("ДокументОбъект.НеявкиИБолезниОрганизаций") Тогда

//если несколько строк в ТЧ, то нужно фиктивно нарисовать им дату окончания
ТЧ=источник.РаботникиОрганизации.Выгрузить();
ТЧ.Сортировать("Сотрудник,ДатаНачала");
НайденоОкончание=Ложь;
ПоследнийП=ТЧ.Количество()-1;
Для п=0 по ПоследнийП Цикл
Сотрудник=ТЧ[п].Сотрудник;
Если п<>ПоследнийП и ТЧ[п].Сотрудник=ТЧ[п+1].Сотрудник Тогда
НайденоОкончание=Истина;
Если ТЧ.Колонки.Найти("ДатаОкончания")=неопределено Тогда
ТЧ.Колонки.Добавить("ДатаОкончания",новый ОписаниеТипов("Дата"));
КонецЕсли;
ТЧ[п].ДатаОкончания=ТЧ[п+1].ДатаНачала-86400;
КонецЕсли;
п=п+1;
КонецЦикла;

Запрос = Новый Запрос;
Запрос.УстановитьПараметр("ТЧ",ТЧ);
Запрос.УстановитьПараметр("ПричинаОтсутствияРаботает",Перечисления.СостоянияРаботникаОрганизации.Работает);

//если есть фиктивная дата окончания
Если НайденоОкончание Тогда
//КОНТРОЛЬ ОШИБОК С ДАТОЙ ЗАВЕРШЕНИЯ
Запрос.Текст = ПолучитьТекстЗапросаДляСостоянияСДатойОкончания();
СообщитьПользователюКонфликтыСДатойОкончания(Запрос);

//удаляем строки с датой завершения
п=0;
пока п < ТЧ.Количество() Цикл
Если ЗначениеЗаполнено(ТЧ[п].ДатаОкончания) Тогда
ТЧ.Удалить(п);
Иначе
п=п+1;
КонецЕсли;
КонецЦикла;

//КОНТРОЛЬ ОШИБОК БЕЗ ДАТЫ ЗАВЕРШЕНИЯ
Запрос.Текст = вернутьТекстЗапросаДляСостоянияБезДатыОкончания();
СообщитьПользователюКонфликтыБезДатыОкончания(Запрос);

Иначе   //нет фиктивной даты окончания
//КОНТРОЛЬ ОШИБОК БЕЗ ДАТЫ ЗАВЕРШЕНИЯ
Запрос.Текст = вернутьТекстЗапросаДляСостоянияБезДатыОкончания();
СообщитьПользователюКонфликтыБезДатыОкончания(Запрос);
КонецЕсли;



ИначеЕсли ТипЗнч(Источник) = тип("ДокументОбъект.ОтпускаОрганизаций") Тогда

//КОНТРОЛЬ ОШИБОК С ДАТОЙ ЗАВЕРШЕНИЯ
Запрос = Новый Запрос;
Запрос.Текст = ПолучитьТекстЗапросаДляСостоянияСДатойОкончания();

Запрос.УстановитьПараметр("ТЧ",источник.РаботникиОрганизации);
Запрос.УстановитьПараметр("ПричинаОтсутствияРаботает",Перечисления.СостоянияРаботникаОрганизации.Работает);
Запрос.УстановитьПараметр("СостояниеКомандировка",Перечисления.СостоянияРаботникаОрганизации.ОтпускЕжегодный);

СообщитьПользователюКонфликтыСДатойОкончания(Запрос);



ИначеЕсли ТипЗнч(Источник) = тип("ДокументОбъект.КомандировкиОрганизаций") Тогда

//запрос с указанной датой окончания
Запрос = Новый Запрос;


//причина отсутствия у этого документа - командировка
ТЗ=источник.РаботникиОрганизации.Выгрузить();
ТЗ.Колонки.Добавить("ПричинаОтсутствия",новый ОписаниеТипов("ПеречислениеСсылка.СостоянияРаботникаОрганизации"));
ТЗ.ЗаполнитьЗначения(Перечисления.СостоянияРаботникаОрганизации, "ПричинаОтсутствия");


Запрос.УстановитьПараметр("ТЧ",ТЗ);
Запрос.УстановитьПараметр("ПричинаОтсутствияРаботает",Перечисления.СостоянияРаботникаОрганизации.Работает);

//КОНТРОЛЬ ОШИБОК С ДАТОЙ ЗАВЕРШЕНИЯ
Запрос.Текст = ПолучитьТекстЗапросаДляСостоянияСДатойОкончания();
СообщитьПользователюКонфликтыСДатойОкончания(Запрос);


//КОНТРОЛЬ ОШИБОК БЕЗ ДАТЫ ЗАВЕРШЕНИЯ

//удаляем строки с датой завершения
п=0;
пока п < ТЗ.Количество() Цикл
Если ЗначениеЗаполнено(ТЗ[п].ДатаОкончания) Тогда
ТЗ.Удалить(п);
Иначе
п=п+1;
КонецЕсли;
КонецЦикла;

Запрос.Текст = вернутьТекстЗапросаДляСостоянияБезДатыОкончания();
СообщитьПользователюКонфликтыБезДатыОкончания(Запрос);



КонецЕсли;

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

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

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

Процедура СообщитьПользователюКонфликтыБезДатыОкончания(Запрос)
Результат = Запрос.ВыполнитьПакет();
ВыборкаНаНачало = Результат[3].Выбрать();
Пока ВыборкаНаНачало.Следующий() Цикл
Сообщить("Внимание! "+ВыборкаНаНачало.Сотрудник+" из состояния """+ВыборкаНаНачало.ТекущееСостояние+" ("+Формат(ВыборкаНаНачало.Период,"ДФ=dd.MM.yyyy")+")"" переходт в состояние """+ВыборкаНаНачало.НовоеСостояние+"("+Формат(ВыборкаНаНачало.ДатаНачала,"ДФ=dd.MM.yyyy")+")"" (документ "+ВыборкаНаНачало.Регистратор+")");
КонецЦикла;

ВыборкаПосле = Результат[4].Выбрать();
Пока ВыборкаПосле.Следующий() Цикл
Сообщить("Внимание! "+ВыборкаПосле.Сотрудник+" : состояние """+ВыборкаПосле.НовоеСостояние+" ("+Формат(ВыборкаПосле.ДатаНачала,"ДФ=dd.MM.yyyy")+")"" будет изменено на """+ВыборкаПосле.ТекущееСостояние+" ("+Формат(ВыборкаПосле.Период,"ДФ=dd.MM.yyyy")+")"" (документ "+ВыборкаПосле.Регистратор+")");
КонецЦикла;

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

Процедура СообщитьПользователюКонфликтыСДатойОкончания(Запрос)
Результат = Запрос.ВыполнитьПакет();
ВыборкаНаНачало = Результат[3].Выбрать();
Пока ВыборкаНаНачало.Следующий() Цикл
Сообщить("Внимание! "+ВыборкаНаНачало.Сотрудник+" из состояния """+ВыборкаНаНачало.ТекущееСостояние+"("+Формат(ВыборкаНаНачало.ДатаТекущегоСостояния,"ДФ=dd.MM.yyyy")+")"" переходт в состояние """+ВыборкаНаНачало.НовоеСостояние+" ("+Формат(ВыборкаНаНачало.ДатаНачала,"ДФ=dd.MM.yyyy")+")"" (документ "+ВыборкаНаНачало.Регистратор+")");
КонецЦикла;

ВыборкаВМомент = Результат[4].Выбрать();
Пока ВыборкаВМомент.Следующий() Цикл
Сообщить("Внимание! "+ВыборкаВМомент.Сотрудник+" "+Формат(ВыборкаВМомент.ДатаИзменения,"ДФ=dd.MM.yyyy")+" переходт в состояние """+ВыборкаВМомент.ТекущееСостояние+""" (документ "+ВыборкаВМомент.Регистратор+")");
КонецЦикла;

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

21 Comments

  1. pattyx

    Склоняюсь к тому, чтобы подобным образом сделать у нас. Кадры утверждают, что раньше кадровые «Неявки и болезни» не проводились, ругались, если для сотрудника был введен документ «Отпуска организаций»). Скажите пожалуйста, так ли это, если существует типовой механизм, то как его настроить?

    Reply
  2. smirnov0ser

    Если неявка и отпуск будут введены одним днем, тогда программа будет ругаться. Так как оба документа пишут данные в регистр СостояниеРаботниковОрганизаций с периодичностью день.

    А данные подписки на события позволят видеть, какое состояние прерываем мы новым приказом, и какие состояния прервут наш приказ.

    Типового механизма нет.

    Reply
  3. FreeArcher

    (1) Вот и мне тоже высказали, что раньше или, где то там у них программа ругалась, а у меня не ругается…

    Придется че-то приколхозить, чтобы не быть плохим программистом, а то у других то ругается, а у меня нет ))

    Reply
  4. FreeArcher

    Вот тут запрос недописан:

        |ВЫБРАТЬ
    |    ТаблицаТЧ.Сотрудник,
    |    ЕСТЬNULL(ВЫБОР
    |            КОГДА СостояниеРаботниковОрганизаций.ПериодЗавершения <> ДАТАВРЕМЯ(1, 1, 1)
    |                    И СостояниеРаботниковОрганизаций.ПериодЗавершения  ДАТАВРЕМЯ(1, 1, 1)
    |                    И СостояниеРаботниковОрганизаций.ПериодЗавершения  ДАТАВРЕМЯ(1, 1, 1)
    |                    И СостояниеРаботниковОрганизаций.ПериодЗавершения  ДАТАВРЕМЯ(1, 1, 1)
    |                        И СостояниеРаботниковОрганизаций.ПериодЗавершения  &ПричинаОтсутствияРаботает

    надо тогда и что там будет

    Reply
  5. smirnov0ser

    (4) FreeArcher,

    И правда, почему-то при сохранении публикации код запроса портится (независимо от раскраски).

    Скачай файл или, если хочешь, вышлю код в личку.

    P.S. Обновил публикацию, теперь корректно отрабатываются несколько строк по сотруднику в одном документе.

    Reply
  6. FreeArcher

    (5) Спасибо, а вот ты не решал эту же проблему, только если зарплатчики не пользуются кадровыми документами, а только «Расчет отпуска сотрудников организации» и т.д. ?

    Reply
  7. adelya

    (6) для этого в настройках Учетной политики на закладке расчет зарплаты есть параметр контролировать пересечение периодов начислений контролирующих норму времени

    Reply
  8. adelya

    Очень нужная вещь, у нас примерно 5000 сотрудников, а так как в программе нет контроля пересечения периодов по кадрам, (как правильно заметил автор — если период не один день), просто необходима. Интегрировала в нашу УПП 1.3 — проверила на двух документах — Все работает!. без единого вмешательства в процедуру. Огромное спасибо автору. Завтра покажу кадрам 🙂

    Reply
  9. MisSus

    Спасибо большое, обработка работает хорошо.

    Хотелось ещё (если возможно), чтобы при наложении периодов, документ всё-же не проводился. А то о наложении периодов сообщает, но при этом документ всё же проводится.

    Reply
  10. smirnov0ser

    (9), Подразумевается, что пересечение периодов не всегда является ошибкой. Но можно и запрещать проводить:

    в процедуры СообщитьПользователюКонфликтыБезДатыОкончания(Запрос) и СообщитьПользователюКонфликтыСДатойОкончания(Запрос) передавайте «Отказ», а при наступлении ошибки нужного вида присваивайте Отказ=Истина;

    Reply
  11. AndrewVVS

    (5) smirnov0ser, а можешь в самой публикации данный текст запроса подредактировать, а то просто «взрыв мозга» происходит при анализе этого куска запроса )). Или в комменте написать, что там конкретно имелось ввиду? А вариант переделки типового запроса — функции «СформироватьЗапросПоРаботникиОрганизации» — не рассматривали?

    Reply
  12. smirnov0ser

    (11)AndrewVVS, при сохранении публикации текст запроса «портится», ничего не могу поделать — качай файл.

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

    Reply
  13. 00alenka00

    В ОбработкаПроведенияКонтрольПересеченияНеявкиИБолезни(Источник,Отказ, Режим) при обработке документа Коммандировки организаций не дописал причину отсутствия.

    И в публикации и в файле тоже самое.

    ТЗ.ЗаполнитьЗначения(Перечисления.СостоянияРаботникаОрганизации, «ПричинаОтсутствия»);

    Значение перечисления Командировка не выбрано.

    Reply
  14. 00alenka00

    Еще переделала выдаваемые сообщение на 1совскую обработку комментариев.

    Там много удобнее, в документы пересекающиеся сразу по ссылке зайти можно.

    Reply
  15. smirnov0ser

    (13)00alenka00, действительно, не дописал. Но это значение используется только при выводе сообщения).

    Исправлю при следующем обновлении

    Reply
  16. Hawk_sib

    спасибо, хорошая идея по реализации, от себя могу сказать, что регистр сведений «состояния работников организаций» в зупе хороший, но выходит проконтроировать до конца у разработчиков его не получилось, сам не так давно столкнулся несколько с иной проблемой http://infostart.ru/public/316095/

    Reply
  17. doctorov_s

    Кто то может скинуть нормальный код на почту doctorov_s@mail.ru заранее спасибо!

    Reply
  18. bpirate999

    Здравствуйте

    У вас есть готовый файл Проверка пересечения кадровых неявок, отпусков, командировок? Могли бы поделиться

    Reply
  19. DomenER

    Тоже интересует недостающий кусок запроса. Может напишите его в комментарии хотя бы?

    Reply
  20. DomenER

    Если автор непротив, то вот общий файл

    Reply
  21. DomenER

    Выявлено, что эта обработка отрабатывает несовсем верно.

    ЗУП 2.5

    При проведении документа «Неявки и болезни» нужно проверить пересечение периодов с периодами отпусков.

    Предположим заведён больничный с 05.08.2017 по 10.08.2017. И 2-й больничный на этого сотрудника с 11.08.2017 по 15.08.2017.

    При необходимости снова провести 1-й больничный появится уведомление об изменении состояния с «Болеет» на «Болеет». Пересечения периодов здесь нет. Больничный просто продолжился, а соответственно нет необходимости уведомлять сотрудника отдела кадров об этом, чтобы не сбивать с толку.

    В конце запроса (тот который без окончательной даты) изменил так:

    ИЗ

    Таблица КАК Таблица

    ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.СостояниеРаботниковОрганизаций КАК СостояниеРаботниковОрганизаций

    ПО Таблица.Сотрудник = СостояниеРаботниковОрганизаций.Сотрудник

    И (СостояниеРаботниковОрганизаций.Период МЕЖДУ Таблица.ДатаНачала И Таблица.ДатаОкончания)

    Условие с больничным всегда будет ложь даже если дата окончания больничного у всех будет проставлено.

    Если п<>ПоследнийП и ТЧ[п].Сотрудник=ТЧ[п+1].Сотрудник Тогда

    НайденоОкончание=Истина;

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *