При формировании отчетов по дням рождения сотрудников в системе 1С:Зарплата и Управление Персоналом (ЗУП) часто возникает вопрос о корректности отображения дат рождения, приходящихся на 29 февраля. Основная проблема, как правило, заключается в том, что в невисокосные годы этой даты календарно не существует, и пользователи опасаются, что сотрудники, родившиеся 29 февраля, могут быть пропущены в отчетах или напоминаниях. Мы рассмотрим, как эта ситуация обрабатывается в типовых решениях 1С и как можно создать собственный запрос для максимально точного и надежного отчета.
Первоначально важно понимать, что современные версии 1С:ЗУП, как правило, уже обладают встроенными механизмами для корректной обработки таких дат. Однако, если требуется специфическая логика или тонкая настройка, нам придется углубиться в принципы работы с датами в языке запросов 1С.
Прежде чем приступать к написанию собственного запроса, мы всегда рекомендуем рассмотреть возможности типовых отчетов, предоставляемых конфигурацией 1С:ЗУП. В большинстве случаев они уже содержат необходимый функционал и корректно обрабатывают специфические даты, такие как 29 февраля.
Одним из наиболее подходящих типовых отчетов для получения информации о днях рождения является "Личные данные сотрудников". Давайте разберем, как его использовать и настроить:
Сотрудник (или ФизическоеЛицо)Дата рожденияДолжностьПодразделение (если необходимо)Дате рождения. Однако для корректной обработки 29 февраля и вывода "ближайших" дней рождения, этот отбор может быть не совсем интуитивным.Подразделению или Должности, если это необходимо. Отсортируйте отчет по Дате рождения, чтобы видеть ближайшие события.Таким образом, для большинства пользователей и задач типового функционала "Личные данные сотрудников" будет достаточно, и нет необходимости изобретать велосипед.
Если же типовой отчет не подходит под ваши уникальные требования, например, нужна очень специфическая логика отбора или форматирования, мы можем создать свой запрос на языке 1С. Здесь ключевым моментом будет правильная работа с датами, особенно с 29 февраля.
Критически важно понимать, как система 1С работает с "несуществующими" датами. Если мы пытаемся создать дату, например, 29 февраля в невисокосный год (который имеет 365 дней), функции 1С автоматически скорректируют эту дату. В большинстве случаев ДОБАВИТЬКДАТЕ() или явное создание даты (например, ДАТА(Год, Месяц, День)) для 29 февраля невисокосного года приведет к результату 28 февраля того же года. Это происходит, потому что система "перематывает" дни и останавливается на последнем возможном дне месяца.
Этот механизм является нашим союзником. Нам не нужно писать сложные проверки на високосность для каждой даты рождения. Достаточно сформировать "виртуальную" дату дня рождения сотрудника в текущем (или интересующем нас) году, и система сама позаботится о коррекции 29 февраля.
Для создания запроса нам понадобятся данные о сотрудниках и их датах рождения. Эти данные обычно хранятся в регистре сведений РаботникиОрганизаций.СрезПоследних или КадроваяИсторияСотрудников.СрезПоследних и справочнике ФизическиеЛица.
Рассмотрим шаги по формированию запроса:
РаботникиОрганизаций.СрезПоследних, чтобы получить актуальные данные о работающих сотрудниках на определенную дату.ФизическиеЛица, на который ссылается регистр РаботникиОрганизаций.&ДатаОтчета), а месяц и день — из даты рождения физического лица.Давайте посмотрим на пример запроса. Мы будем использовать параметры &ДатаОтчета (обычно текущая дата или дата, на которую формируется отчет), &НачалоПериода и &КонецПериода (диапазон, за который мы хотим получить дни рождения, например, ближайшие 30 дней).
Представим, что нам нужно получить список сотрудников, у которых день рождения выпадает в ближайшие 30 дней от текущей даты, и при этом корректно учесть 29 февраля.
Параметры запроса:
&ДатаОтчета: Дата, относительно которой строится отчет (например, текущая дата). Тип: Дата.&НачалоПериода: Начало интервала для поиска дней рождения (например, ДОБАВИТЬКДАТЕ(&ДатаОтчета, ДЕНЬ, 0)). Тип: Дата.&КонецПериода: Конец интервала для поиска дней рождения (например, ДОБАВИТЬКДАТЕ(&ДатаОтчета, ДЕНЬ, 29) для 30 дней). Тип: Дата.Важно: Запрос, который был приведен в сообщении 11, использует Период МЕЖДУ &ДатаНач И ДОБАВИТЬКДАТЕ(&ДатаКон, ДЕНЬ, 1) для регистра КадроваяИсторияСотрудников.СрезПоследних. Это неверно, так как поле Период в срезах регистров сведений, как правило, является датой записи или актуальности записи, а не датой рождения. Мы будем обращаться напрямую к ФизическоеЛицо.ДатаРождения.
ВЫБРАТЬ
РаботникиОрганизацийСрезПоследних.Сотрудник.Наименование КАК Сотрудник,
РаботникиОрганизацийСрезПоследних.ФизическоеЛицо.ДатаРождения КАК ДатаРожденияФизЛица,
РаботникиОрганизацийСрезПоследних.Должность КАК Должность,
РаботникиОрганизацийСрезПоследних.Подразделение КАК Подразделение,
РаботникиОрганизацийСрезПоследних.ГоловнаяОрганизация КАК Организация,
ВЫБОР
КОГДА МЕСЯЦ(РаботникиОрганизацийСрезПоследних.ФизическоеЛицо.ДатаРождения) = 2
И ДЕНЬ(РаботникиОрганизацийСрезПоследних.ФизическоеЛицо.ДатаРождения) = 29
И НЕ ГОДВИСОКОСНЫЙ(ГОД(&ДатаОтчета)) // Если год отчета не високосный
ТОГДА ДОБАВИТЬКДАТЕ(
ДОБАВИТЬКДАТЕ(НАЧАЛОПЕРИОДА(&ДатаОтчета, ГОД), МЕСЯЦ(РаботникиОрганизацийСрезПоследних.ФизическоеЛицо.ДатаРождения) - 1, МЕСЯЦ),
ДЕНЬ(РаботникиОрганизацийСрезПоследних.ФизическоеЛицо.ДатаРождения) - 2, ДЕНЬ // Получаем 28 февраля
)
ИНАЧЕ ДОБАВИТЬКДАТЕ(
ДОБАВИТЬКДАТЕ(НАЧАЛОПЕРИОДА(&ДатаОтчета, ГОД), МЕСЯЦ(РаботникиОрганизацийСрезПоследних.ФизическоеЛицо.ДатаРождения) - 1, МЕСЯЦ),
ДЕНЬ(РаботникиОрганизацийСрезПоследних.ФизическоеЛицо.ДатаРождения) - 1, ДЕНЬ
)
КОНЕЦ КАК ДатаДняРожденияВГодуОтчета
ИЗ
РегистрСведений.РаботникиОрганизаций.СрезПоследних(
&ДатаОтчета,
// Условие на активность сотрудника
Активность = ИСТИНА
) КАК РаботникиОрганизацийСрезПоследних
ГДЕ
// Фильтруем по дате дня рождения в текущем году отчета
// Сравниваем полученную "виртуальную" дату дня рождения с заданным диапазоном.
// Если день рождения уже прошел в текущем году, мы можем проверить его в следующем году
// для корректного отображения "ближайших" дней рождения.
(
(ВЫБОР
КОГДА МЕСЯЦ(РаботникиОрганизацийСрезПоследних.ФизическоеЛицо.ДатаРождения) = 2
И ДЕНЬ(РаботникиОрганизацийСрезПоследних.ФизическоеЛицо.ДатаРождения) = 29
И НЕ ГОДВИСОКОСНЫЙ(ГОД(&ДатаОтчета))
ТОГДА ДОБАВИТЬКДАТЕ(
ДОБАВИТЬКДАТЕ(НАЧАЛОПЕРИОДА(&ДатаОтчета, ГОД), МЕСЯЦ(РаботникиОрганизацийСрезПоследних.ФизическоеЛицо.ДатаРождения) - 1, МЕСЯЦ),
ДЕНЬ(РаботникиОрганизацийСрезПоследних.ФизическоеЛицо.ДатаРождения) - 2, ДЕНЬ
)
ИНАЧЕ ДОБАВИТЬКДАТЕ(
ДОБАВИТЬКДАТЕ(НАЧАЛОПЕРИОДА(&ДатаОтчета, ГОД), МЕСЯЦ(РаботникиОрганизацийСрезПоследних.ФизическоеЛицо.ДатаРождения) - 1, МЕСЯЦ),
ДЕНЬ(РаботникиОрганизацийСрезПоследних.ФизическоеЛицо.ДатаРождения) - 1, ДЕНЬ
)
КОНЕЦ МЕЖДУ &НачалоПериода И &КонецПериода)
ИЛИ
// Учет дней рождения, которые уже прошли в текущем году, но наступят в следующем
// если период &НачалоПериода - &КонецПериода пересекает конец/начало года.
// Для простых "ближайших 30 дней" этот блок может быть излишним,
// но для "всех дней рождения в текущем квартале" он может потребоваться,
// если диапазон отчета начинается в одном году и заканчивается в следующем.
// Для демонстрации "ближайших 30 дней" мы просто ищем в текущем году.
(ДОБАВИТЬКДАТЕ(
ВЫБОР
КОГДА МЕСЯЦ(РаботникиОрганизацийСрезПоследних.ФизическоеЛицо.ДатаРождения) = 2
И ДЕНЬ(РаботникиОрганизацийСрезПоследних.ФизическоеЛицо.ДатаРождения) = 29
И НЕ ГОДВИСОКОСНЫЙ(ГОД(ДОБАВИТЬКДАТЕ(&ДатаОтчета, ГОД, 1))) // Если СЛЕДУЮЩИЙ год не високосный
ТОГДА ДОБАВИТЬКДАТЕ(
ДОБАВИТЬКДАТЕ(НАЧАЛОПЕРИОДА(ДОБАВИТЬКДАТЕ(&ДатаОтчета, ГОД, 1), ГОД), МЕСЯЦ(РаботникиОрганизацийСрезПоследних.ФизическоеЛицо.ДатаРождения) - 1, МЕСЯЦ),
ДЕНЬ(РаботникиОрганизацийСрезПоследних.ФизическоеЛицо.ДатаРождения) - 2, ДЕНЬ
)
ИНАЧЕ ДОБАВИТЬКДАТЕ(
ДОБАВИТЬКДАТЕ(НАЧАЛОПЕРИОДА(ДОБАВИТЬКДАТЕ(&ДатаОтчета, ГОД, 1), ГОД), МЕСЯЦ(РаботникиОрганизацийСрезПоследних.ФизическоеЛицо.ДатаРождения) - 1, МЕСЯЦ),
ДЕНЬ(РаботникиОрганизацийСрезПоследних.ФизическоеЛицо.ДатаРождения) - 1, ДЕНЬ
)
КОНЕЦ,
ДЕНЬ, 0
) МЕЖДУ &НачалоПериода И &КонецПериода И ГОД(ДОБАВИТЬКДАТЕ(&ДатаОтчета, ГОД, 1)) = ГОД(&КонецПериода))
)
УПОРЯДОЧИТЬ ПО
ДатаДняРожденияВГодуОтчета
РегистрСведений.РаботникиОрганизаций.СрезПоследних(&ДатаОтчета, Активность = ИСТИНА). Это позволяет нам получить список всех активных сотрудников на указанную дату отчета &ДатаОтчета. Важно передавать &ДатаОтчета как параметр в срез последних, чтобы данные были актуальны.ДатаДняРожденияВГодуОтчета: Это ключевое поле. Мы конструируем дату дня рождения сотрудника, используя год из &ДатаОтчета, а месяц и день из ФизическоеЛицо.ДатаРождения.
НАЧАЛОПЕРИОДА(&ДатаОтчета, ГОД): Получаем 1 января года, в котором формируется отчет.ДОБАВИТЬКДАТЕ(..., МЕСЯЦ(ДатаРождения) - 1, МЕСЯЦ): Прибавляем необходимое количество месяцев, чтобы получить 1-е число месяца рождения. Например, для марта (3-й месяц) прибавляем 2 месяца к 1 января.ДОБАВИТЬКДАТЕ(..., ДЕНЬ(ДатаРождения) - 1, ДЕНЬ): Прибавляем необходимое количество дней, чтобы получить точный день рождения в этом месяце.Особый случай 29 февраля: Внутри ВЫБОР КОГДА ... ТОГДА ... ИНАЧЕ ... КОНЕЦ мы явно проверяем, является ли текущий год отчета невисокосным (НЕ ГОДВИСОКОСНЫЙ(ГОД(&ДатаОтчета))), и если да, то для дат 29 февраля мы принудительно устанавливаем день 28 (ДЕНЬ(ФизическоеЛицо.ДатаРождения) - 2, ДЕНЬ), что в конечном итоге даст 28 февраля. В противном случае (если год високосный или день рождения не 29 февраля), используется стандартная логика.
ГДЕ: Здесь мы фильтруем сотрудников. Основная часть условия ДатаДняРожденияВГодуОтчета МЕЖДУ &НачалоПериода И &КонецПериода выбирает всех, чья "виртуальная" дата дня рождения попадает в заданный нами интервал.
Дополнительная секция ИЛИ, включающая в себя логику для дня рождения в следующем году, необходима, если диапазон отчета может охватывать границу годов. Например, если мы ищем дни рождения с 20 декабря текущего года по 20 января следующего года, то дни рождения, которые приходятся на январь следующего года, также должны быть учтены. Для простого отчета "ближайших X дней" такой усложненный вариант может быть избыточен, и достаточно первой части условия.
ДатаДняРожденияВГодуОтчета, что позволяет видеть предстоящие дни рождения в хронологическом порядке.При использовании этого запроса в конструкторе запросов или в модуле вы должны будете передать значения для параметров &ДатаОтчета, &НачалоПериода и &КонецПериода.
В заключение, проблема учета 29 февраля при формировании отчетов по дням рождения в 1С:ЗУП вполне решаема. В большинстве случаев достаточно использовать типовой функционал, который уже обрабатывает такие нюансы. Если же требуется индивидуальное решение, то, следуя принципам работы с датами в языке запросов 1С и используя описанную логику, вы сможете создать надежный и корректный отчет.