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

Опубликовал Сергей Смирнов (smirnov0ser) в раздел Программирование - Практика программирования

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

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

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

Скачать файлы

Наименование Файл Версия Размер
Новый общий модуль
.txt 15,01Kb
20.12.13
96
.txt 15,01Kb 96 Скачать

См. также

Вознаграждение за ответ
Показать полностью
Комментарии
1. наташа (pattyx) 23.09.13 15:52 Сейчас в теме
Склоняюсь к тому, чтобы подобным образом сделать у нас. Кадры утверждают, что раньше кадровые "Неявки и болезни" не проводились, ругались, если для сотрудника был введен документ "Отпуска организаций"). Скажите пожалуйста, так ли это, если существует типовой механизм, то как его настроить?
2. Сергей Смирнов (smirnov0ser) 64 24.09.13 11:43 Сейчас в теме
Если неявка и отпуск будут введены одним днем, тогда программа будет ругаться. Так как оба документа пишут данные в регистр СостояниеРаботниковОрганизаций с периодичностью день.
А данные подписки на события позволят видеть, какое состояние прерываем мы новым приказом, и какие состояния прервут наш приказ.
Типового механизма нет.
3. Алексей Беспалов (FreeArcher) 51 20.12.13 15:46 Сейчас в теме
(1) Вот и мне тоже высказали, что раньше или, где то там у них программа ругалась, а у меня не ругается...
Придется че-то приколхозить, чтобы не быть плохим программистом, а то у других то ругается, а у меня нет ))
4. Алексей Беспалов (FreeArcher) 51 20.12.13 16:05 Сейчас в теме
Вот тут запрос недописан:
    |ВЫБРАТЬ
            |    ТаблицаТЧ.Сотрудник,
            |    ЕСТЬNULL(ВЫБОР
            |            КОГДА СостояниеРаботниковОрганизаций.ПериодЗавершения <> ДАТАВРЕМЯ(1, 1, 1)
            |                    И СостояниеРаботниковОрганизаций.ПериодЗавершения  ДАТАВРЕМЯ(1, 1, 1)
            |                    И СостояниеРаботниковОрганизаций.ПериодЗавершения  ДАТАВРЕМЯ(1, 1, 1)
            |                    И СостояниеРаботниковОрганизаций.ПериодЗавершения  ДАТАВРЕМЯ(1, 1, 1)
            |                        И СостояниеРаботниковОрганизаций.ПериодЗавершения  &ПричинаОтсутствияРаботает
...Показать Скрыть


надо тогда и что там будет
5. Сергей Смирнов (smirnov0ser) 64 20.12.13 16:59 Сейчас в теме
(4) FreeArcher,
И правда, почему-то при сохранении публикации код запроса портится (независимо от раскраски).
Скачай файл или, если хочешь, вышлю код в личку.

P.S. Обновил публикацию, теперь корректно отрабатываются несколько строк по сотруднику в одном документе.
6. Алексей Беспалов (FreeArcher) 51 20.12.13 19:00 Сейчас в теме
(5) Спасибо, а вот ты не решал эту же проблему, только если зарплатчики не пользуются кадровыми документами, а только "Расчет отпуска сотрудников организации" и т.д. ?
7. Деля (adelya) 12 20.12.13 19:36 Сейчас в теме
(6) для этого в настройках Учетной политики на закладке расчет зарплаты есть параметр контролировать пересечение периодов начислений контролирующих норму времени
FreeArcher; +1 Ответить
8. Деля (adelya) 12 20.12.13 21:14 Сейчас в теме
Очень нужная вещь, у нас примерно 5000 сотрудников, а так как в программе нет контроля пересечения периодов по кадрам, (как правильно заметил автор - если период не один день), просто необходима. Интегрировала в нашу УПП 1.3 - проверила на двух документах - Все работает!. без единого вмешательства в процедуру. Огромное спасибо автору. Завтра покажу кадрам :-)
9. Михаил Суслов (MisSus) 24.02.14 15:06 Сейчас в теме
Спасибо большое, обработка работает хорошо.
Хотелось ещё (если возможно), чтобы при наложении периодов, документ всё-же не проводился. А то о наложении периодов сообщает, но при этом документ всё же проводится.
10. Сергей Смирнов (smirnov0ser) 64 24.02.14 15:31 Сейчас в теме
(9), Подразумевается, что пересечение периодов не всегда является ошибкой. Но можно и запрещать проводить:
в процедуры СообщитьПользователюКонфликтыБезДатыОкончания(Запрос) и СообщитьПользователюКонфликтыСДатойОкончания(Запрос) передавайте "Отказ", а при наступлении ошибки нужного вида присваивайте Отказ=Истина;
11. Андрей Ро (AndrewVVS) 30.06.14 12:08 Сейчас в теме
(5) smirnov0ser, а можешь в самой публикации данный текст запроса подредактировать, а то просто "взрыв мозга" происходит при анализе этого куска запроса )). Или в комменте написать, что там конкретно имелось ввиду? А вариант переделки типового запроса - функции "СформироватьЗапросПоРаботникиОрганизации" - не рассматривали?
12. Сергей Смирнов (smirnov0ser) 64 24.07.14 12:17 Сейчас в теме
(11)AndrewVVS, при сохранении публикации текст запроса "портится", ничего не могу поделать - качай файл.
Не вижу смысла переделывать типовой запрос, в этом случае обновление конфигурации проходит значительно сложнее, чем в случае использования подписки на событие.
13. Алена Андреевна (00alenka00) 24.07.14 17:37 Сейчас в теме
В ОбработкаПроведенияКонтрольПересеченияНеявкиИБолезни(Источник,Отказ, Режим) при обработке документа Коммандировки организаций не дописал причину отсутствия.
И в публикации и в файле тоже самое.
ТЗ.ЗаполнитьЗначения(Перечисления.СостоянияРаботникаОрганизации, "ПричинаОтсутствия");
Значение перечисления Командировка не выбрано.

14. Алена Андреевна (00alenka00) 24.07.14 17:48 Сейчас в теме
Еще переделала выдаваемые сообщение на 1совскую обработку комментариев.
Там много удобнее, в документы пересекающиеся сразу по ссылке зайти можно.
15. Сергей Смирнов (smirnov0ser) 64 25.07.14 12:00 Сейчас в теме
(13)00alenka00, действительно, не дописал. Но это значение используется только при выводе сообщения).
Исправлю при следующем обновлении
18. Степан Шушаков (Hawk_sib) 13 24.12.14 12:50 Сейчас в теме
спасибо, хорошая идея по реализации, от себя могу сказать, что регистр сведений "состояния работников организаций" в зупе хороший, но выходит проконтроировать до конца у разработчиков его не получилось, сам не так давно столкнулся несколько с иной проблемой http://infostart.ru/public/316095/
19. Сергей Докторов (doctorov_s) 41 17.09.15 10:02 Сейчас в теме
Кто то может скинуть нормальный код на почту doctorov_s@mail.ru заранее спасибо!
20. Евгений Чудов (bpirate999) 15 24.08.16 13:22 Сейчас в теме
Здравствуйте
У вас есть готовый файл Проверка пересечения кадровых неявок, отпусков, командировок? Могли бы поделиться