В работе кадрового отдела часто возникают ситуации, когда пересекаются периоды отпусков, командировок или болезней.
Например, больничный сотрудника 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")+" переходт в состояние """+ВыборкаВМомент.ТекущееСостояние+""" (документ "+ВыборкаВМомент.Регистратор+")");
КонецЦикла;
КонецПроцедуры





Склоняюсь к тому, чтобы подобным образом сделать у нас. Кадры утверждают, что раньше кадровые «Неявки и болезни» не проводились, ругались, если для сотрудника был введен документ «Отпуска организаций»). Скажите пожалуйста, так ли это, если существует типовой механизм, то как его настроить?
Если неявка и отпуск будут введены одним днем, тогда программа будет ругаться. Так как оба документа пишут данные в регистр СостояниеРаботниковОрганизаций с периодичностью день.
А данные подписки на события позволят видеть, какое состояние прерываем мы новым приказом, и какие состояния прервут наш приказ.
Типового механизма нет.
(1) Вот и мне тоже высказали, что раньше или, где то там у них программа ругалась, а у меня не ругается…
Придется че-то приколхозить, чтобы не быть плохим программистом, а то у других то ругается, а у меня нет ))
Вот тут запрос недописан:
надо тогда и что там будет
(4) FreeArcher,
И правда, почему-то при сохранении публикации код запроса портится (независимо от раскраски).
Скачай файл или, если хочешь, вышлю код в личку.
P.S. Обновил публикацию, теперь корректно отрабатываются несколько строк по сотруднику в одном документе.
(5) Спасибо, а вот ты не решал эту же проблему, только если зарплатчики не пользуются кадровыми документами, а только «Расчет отпуска сотрудников организации» и т.д. ?
(6) для этого в настройках Учетной политики на закладке расчет зарплаты есть параметр контролировать пересечение периодов начислений контролирующих норму времени
Очень нужная вещь, у нас примерно 5000 сотрудников, а так как в программе нет контроля пересечения периодов по кадрам, (как правильно заметил автор — если период не один день), просто необходима. Интегрировала в нашу УПП 1.3 — проверила на двух документах — Все работает!. без единого вмешательства в процедуру. Огромное спасибо автору. Завтра покажу кадрам 🙂
Спасибо большое, обработка работает хорошо.
Хотелось ещё (если возможно), чтобы при наложении периодов, документ всё-же не проводился. А то о наложении периодов сообщает, но при этом документ всё же проводится.
(9), Подразумевается, что пересечение периодов не всегда является ошибкой. Но можно и запрещать проводить:
в процедуры СообщитьПользователюКонфликтыБезДатыОкончания(Запрос) и СообщитьПользователюКонфликтыСДатойОкончания(Запрос) передавайте «Отказ», а при наступлении ошибки нужного вида присваивайте Отказ=Истина;
(5) smirnov0ser, а можешь в самой публикации данный текст запроса подредактировать, а то просто «взрыв мозга» происходит при анализе этого куска запроса )). Или в комменте написать, что там конкретно имелось ввиду? А вариант переделки типового запроса — функции «СформироватьЗапросПоРаботникиОрганизации» — не рассматривали?
(11)AndrewVVS, при сохранении публикации текст запроса «портится», ничего не могу поделать — качай файл.
Не вижу смысла переделывать типовой запрос, в этом случае обновление конфигурации проходит значительно сложнее, чем в случае использования подписки на событие.
В ОбработкаПроведенияКонтрольПересеченияНеявкиИБолезни(Источник,Отказ, Режим) при обработке документа Коммандировки организаций не дописал причину отсутствия.
И в публикации и в файле тоже самое.
ТЗ.ЗаполнитьЗначения(Перечисления.СостоянияРаботникаОрганизации, «ПричинаОтсутствия»);
Значение перечисления Командировка не выбрано.
Еще переделала выдаваемые сообщение на 1совскую обработку комментариев.
Там много удобнее, в документы пересекающиеся сразу по ссылке зайти можно.
(13)00alenka00, действительно, не дописал. Но это значение используется только при выводе сообщения).
Исправлю при следующем обновлении
спасибо, хорошая идея по реализации, от себя могу сказать, что регистр сведений «состояния работников организаций» в зупе хороший, но выходит проконтроировать до конца у разработчиков его не получилось, сам не так давно столкнулся несколько с иной проблемой
Кто то может скинуть нормальный код на почту doctorov_s@mail.ru заранее спасибо!
Здравствуйте
У вас есть готовый файл Проверка пересечения кадровых неявок, отпусков, командировок? Могли бы поделиться
Тоже интересует недостающий кусок запроса. Может напишите его в комментарии хотя бы?
Если автор непротив, то вот общий файл
Выявлено, что эта обработка отрабатывает несовсем верно.
ЗУП 2.5
При проведении документа «Неявки и болезни» нужно проверить пересечение периодов с периодами отпусков.
Предположим заведён больничный с 05.08.2017 по 10.08.2017. И 2-й больничный на этого сотрудника с 11.08.2017 по 15.08.2017.
При необходимости снова провести 1-й больничный появится уведомление об изменении состояния с «Болеет» на «Болеет». Пересечения периодов здесь нет. Больничный просто продолжился, а соответственно нет необходимости уведомлять сотрудника отдела кадров об этом, чтобы не сбивать с толку.
В конце запроса (тот который без окончательной даты) изменил так:
ИЗ
Таблица КАК Таблица
ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.СостояниеРаботниковОрганизаций КАК СостояниеРаботниковОрганизаций
ПО Таблица.Сотрудник = СостояниеРаботниковОрганизаций.Сотрудник
И (СостояниеРаботниковОрганизаций.Период МЕЖДУ Таблица.ДатаНачала И Таблица.ДатаОкончания)
Условие с больничным всегда будет ложь даже если дата окончания больничного у всех будет проставлено.
Если п<>ПоследнийП и ТЧ[п].Сотрудник=ТЧ[п+1].Сотрудник Тогда
НайденоОкончание=Истина;