В практике разработчика 1С часто возникает задача определения точного возраста физического лица или сотрудника непосредственно в тексте запроса. Это необходимо для построения отчетов по кадровому составу, анализа возрастных групп (например, чтобы составить список детей сотрудников на новогодние подарки для ЗУП) или автоматического формирования поздравлений, для чего удобно использовать отчет Дни рождения сотрудников. Однако стандартная функция РАЗНОСТЬДАТ при использовании параметра ГОД работает по принципу разницы календарных лет, что приводит к ошибкам в расчетах, если день рождения в текущем году еще не наступил. В этой статье мы подробно разберем, как обойти это ограничение и получить математически точный результат.
Рассмотрим ситуацию: сотрудник родился 31 декабря 1990 года. Сегодня — 1 января 2024 года. Очевидно, что полных лет сотруднику всё еще 33, так как его 34-летие наступит только в конце года. Если мы воспользуемся простейшей конструкцией в запросе (проверить которую поможет консоль запросов ЗУП — см. профессиональная консоль запросов и СКД для 1С):
ВЫБРАТЬ
РАЗНОСТЬДАТ(Сотрудники.ДатаРождения, &ТекущаяДата, ГОД) КАК Возраст
ИЗ
Справочник.Сотрудники КАК Сотрудники
Система вернет результат 34. Функция РАЗНОСТЬДАТ в 1С просто вычитает номер года одной даты из номера года другой. Это базовый принцип, но для более сложных вычислений стажа или возраста существуют продвинутые приемы работы с датами. Для получения корректного значения в нашем случае необходимо проанализировать, наступил ли уже день рождения в году, указанном в параметре &ТекущаяДата. Проанализируем, как реализовать эту проверку программными средствами языка запросов.
Чтобы вычислить количество полных лет, нам нужно реализовать логическую проверку: если дата рождения, «перенесенная» на количество лет разницы, оказывается больше текущей даты, значит, текущий год еще «не завершен» для возраста, и из полученной разницы нужно вычесть единицу. Этот же принцип используется, когда требуется внедрить контроль срока действия паспортов (20/45 лет) для ЗУП. Разберем этот алгоритм по шагам.
Шаг 1. Получение базовой разницы лет. Сначала мы вычисляем разницу между годами, как в примере выше.
Шаг 2. Использование функции ДОБАВИТЬКДАТЕ. Мы прибавляем полученную разницу к исходной дате рождения. Это позволяет нам увидеть, какой была бы дата рождения в текущем году.
Шаг 3. Сравнение с целевой датой. Если полученная дата больше нашей контрольной даты (например, &ТекущаяДата или &Период), мы понимаем, что человек еще не отпраздновал день рождения в этом году.
Рассмотрим подробнее итоговую конструкцию, которую можно использовать в любом запросе. Мы будем использовать оператор ВЫБОР для корректировки результата.
ВЫБРАТЬ
Сотрудники.Ссылка КАК Сотрудник,
Сотрудники.ДатаРождения КАК ДатаРождения,
ВЫБОР
КОГДА ДОБАВИТЬКДАТЕ(Сотрудники.ДатаРождения, ГОД, РАЗНОСТЬДАТ(Сотрудники.ДатаРождения, &ТекущаяДата, ГОД)) > &ТекущаяДата
ТОГДА РАЗНОСТЬДАТ(Сотрудники.ДатаРождения, &ТекущаяДата, ГОД) - 1
ИНАЧЕ РАЗНОСТЬДАТ(Сотрудники.ДатаРождения, &ТекущаяДата, ГОД)
КОНЕЦ КАК ВозрастПолныхЛет
ИЗ
Справочник.Сотрудники КАК Сотрудники
ГДЕ
Сотрудники.ДатаРождения <> ДАТАВРЕМЯ(1, 1, 1)
В этом примере мы сначала проверяем, заполнена ли ДатаРождения, чтобы избежать некорректных вычислений для пустых значений. Основная логика заключена в блоке ВЫБОР. Если прибавление разницы лет к дате рождения дает дату «в будущем» относительно &ТекущаяДата, мы отнимаем единицу. В противном случае оставляем разницу лет как есть.
Выясним причину, по которой этот метод считается наиболее надежным. Платформа 1С корректно обрабатывает високосные годы. Если сотрудник родился 29 февраля, то при использовании функции ДОБАВИТЬКДАТЕ для невисокосного года система автоматически вернет 28 февраля. Это позволяет избежать ошибок «вылета» за границы календаря и делает расчет универсальным для любых дат.
Иногда требуется рассчитать возраст не на текущий момент, а на дату какого-либо события (например, на дату приема на работу или дату увольнения). В этом случае мы просто заменяем параметр &ТекущаяДата на соответствующее поле из информационной базы. Посмотрим на пример:
ВЫБРАТЬ
ПриемНаРаботу.Сотрудник,
ВЫБОР
КОГДА ДОБАВИТЬКДАТЕ(ПриемНаРаботу.Сотрудник.ФизическоеЛицо.ДатаРождения, ГОД, РАЗНОСТЬДАТ(ПриемНаРаботу.Сотрудник.ФизическоеЛицо.ДатаРождения, ПриемНаРаботу.Дата, ГОД)) > ПриемНаРаботу.Дата
ТОГДА РАЗНОСТЬДАТ(ПриемНаРаботу.Сотрудник.ФизическоеЛицо.ДатаРождения, ПриемНаРаботу.Дата, ГОД) - 1
ИНАЧЕ РАЗНОСТЬДАТ(ПриемНаРаботу.Сотрудник.ФизическоеЛицо.ДатаРождения, ПриемНаРаботу.Дата, ГОД)
КОНЕЦ КАК ВозрастНаМоментПриема
ИЗ
Документ.ПриемНаРаботу КАК ПриемНаРаботу
Такой подход позволяет строить сложные аналитические отчеты, а также реализовать ограничение проведения документа «Приём на работу» в 1С:ЗУП при отсутствии обязательных данных о возрасте сотрудника — для этого подойдёт настройка проверок и контроля ввода без доработки 1С.
Если вы разрабатываете отчет на СКД, расчет возраста можно вынести в вычисляемые поля. Однако, как показывает практика, профессиональнее и быстрее с точки зрения производительности делать это сразу в тексте запроса. Вопрос о том, где лучше производить вычисления, — в вычисляемых полях или в запросах, — особенно актуален для сложных кадровых отчетов. Расчет в запросе позволяет использовать поле ВозрастПолныхЛет для группировок и отборов без лишних затрат ресурсов на стороне клиента.
Важный момент: при написании сложных запросов с расчетом возраста старайтесь использовать псевдонимы полей и временные таблицы, если расчет требуется производить для большого объема данных. Это сделает код более читаемым и эффективным, а также упростит отладку.
Подводя итог, мы проанализировали ситуацию с вычислением возраста в 1С и выяснили, что комбинирование функций РАЗНОСТЬДАТ, ДОБАВИТЬКДАТЕ и оператора ВЫБОР является "золотым стандартом" для получения точного значения полных лет. Данный метод исключает погрешности, связанные с днем и месяцем рождения, обеспечивая высокую точность данных в ваших программных продуктах.