Оповещение о предстоящих днях рождения коллег — одна из частых сервисных задач при доработке учетных систем. Пользователям удобно видеть список именинников на ближайшие 3–5 дней сразу при входе в программу. Рассмотрим, как правильно реализовать данный функционал, разберем возможные подводные камни алгоритмов и изучим примеры кода для различных версий платформы 1С.
Прежде чем приступать к написанию кода, проанализируем неочевидные логические ошибки, с которыми часто сталкиваются разработчики:
Дата(ТекущийГод, 2, 29) в невисокосный год приведет к критической ошибке выполнения («Недопустимая дата»). Для таких сотрудников необходимо программно предусматривать сдвиг на 28 февраля или 1 марта.ФизическиеЛица, тогда как признак работы в организации определяется кадровыми срезами справочника Сотрудники или регистрами кадровой истории. Выборка напрямую из физических лиц без проверки статуса работы приведет к выводу уволенных сотрудников.Разберем наиболее производительный вариант для платформы 8.3 — использование языка запросов. Вместо перебора справочников в цикле мы вычисляем смещение года и получаем список сотрудников, чей день рождения попадает в интервал между текущей датой и датой через $N$ дней.
Рассмотрим пример формирования запроса с приведением даты к текущему и следующему годам:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Сотрудники.Ссылка КАК Сотрудник,
| Сотрудники.ФизическоеЛицо КАК ФизЛицо,
| Сотрудники.ФизическоеЛицо.ДатаРождения КАК ДатаРождения,
| ВЫБОР
| КОГДА ДОБАВИТЬКДАТЕ(Сотрудники.ФизическоеЛицо.ДатаРождения, ГОД, РАЗНОСТЬДАТ(Сотрудники.ФизическоеЛицо.ДатаРождения, &ТекущаяДата, ГОД)) < &ТекущаяДата
| ТОГДА ДОБАВИТЬКДАТЕ(Сотрудники.ФизическоеЛицо.ДатаРождения, ГОД, РАЗНОСТЬДАТ(Сотрудники.ФизическоеЛицо.ДатаРождения, &ТекущаяДата, ГОД) + 1)
| ИНАЧЕ ДОБАВИТЬКДАТЕ(Сотрудники.ФизическоеЛицо.ДатаРождения, ГОД, РАЗНОСТЬДАТ(Сотрудники.ФизическоеЛицо.ДатаРождения, &ТекущаяДата, ГОД))
| КОНЕЦ КАК БлижайшийДР
|ПОМЕСТИТЬ ВТ_Именинники
|ИЗ
| Справочник.Сотрудники КАК Сотрудники
|ГДЕ
| НЕ Сотрудники.ВАрхиве
| И Сотрудники.ФизическоеЛицо.ДатаРождения <> ДАТАВРЕМЯ(1, 1, 1)
|;
|
|ВЫБРАТЬ
| ВТ.Сотрудник,
| ВТ.ДатаРождения,
| ВТ.БлижайшийДР
|ИЗ
| ВТ_Именинники КАК ВТ
|ГДЕ
| ВТ.БлижайшийДР МЕЖДУ &ТекущаяДата И &ДатаКонцаПериода
|УПОРЯДОЧИТЬ ПО
| ВТ.БлижайшийДР";
Запрос.УстановитьПараметр("ТекущаяДата", НачалоДня(ТекущаяДата()));
Запрос.УстановитьПараметр("ДатаКонцаПериода", КонецДня(ТекущаяДата() + 5 * 86400));
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
Сообщить("У сотрудника " + Строка(Выборка.Сотрудник) + " день рождения: " + Формат(Выборка.БлижайшийДР, "ДФ=dd.MM"));
КонецЦикла;
Внесение изменений напрямую в модуль управляемого приложения (в процедуру ПриНачалеРаботыСистемы) усложняет последующие обновления типовой конфигурации. Рассмотрим современные и безопасные способы интеграции:
ПриНачалеРаботыСистемы с директивой &После. Конфигурация остается на полной поддержке.НапоминанияПользователя.Посмотрим на классические примеры решения данной задачи в версиях 7.7. Первый вариант строится на переборе элементов справочника в цикле с проверкой даты:
Спр = СоздатьОбъект("Справочник.Сотрудники");
Спр.ВыбратьЭлементы();
Пока Спр.ПолучитьЭлемент() = 1 Цикл
Если Спр.ДатаУвольнения = Дата(0) Тогда
ДатаДР = Дата(ДатаГод(РабочаяДата), ДатаМесяц(Спр.ДатаРождения), ДатаЧисло(Спр.ДатаРождения));
Если (РабочаяДата >= ДатаДР - 5) и (РабочаяДата <= ДатаДР) Тогда
Сообщить("Сотруднику " + Спр.Наименование + " исполняется " + (ДатаГод(РабочаяДата) - ДатаГод(Спр.ДатаРождения)) + " (" + Формат(Спр.ДатаРождения, "Д ДДММММГГГГ") + ")");
КонецЕсли;
КонецЕсли;
КонецЦикла;
Второй вариант для 7.7 использует встроенный объект Запрос, что позволяет фильтровать данные быстрее:
Запрос = СоздатьОбъект("Запрос");
ТекстЗапроса = "
|Обрабатывать НеПомеченныеНаУдаление;
|Без итогов;
|Сотрудник = Справочник.Сотрудники.ТекущийЭлемент;
|ДатаРождения = Справочник.Сотрудники.ДатаРождения;
|Группировка Сотрудник без групп;
|Условие((ДатаЧисло(ДатаРождения) >= ДатаЧисло(РабочаяДата)) и (ДатаЧисло(ДатаРождения) <= ДатаЧисло(РабочаяДата) + 5));
|Условие(ДатаМесяц(ДатаРождения) = ДатаМесяц(РабочаяДата));
|";
Если Запрос.Выполнить(ТекстЗапроса) <> 0 Тогда
Пока Запрос.Группировка() = 1 Цикл
Сообщить("У сотрудника " + Запрос.Сотрудник + " день рождения: " + Запрос.ДатаРождения);
КонецЦикла;
КонецЕсли;
Таким образом, для современных систем 1С 8.3 оптимальным решением является использование запроса с обработкой смещения дат, внедренного через механизм расширений или панель начальной страницы.