При разработке отчетов на Системе Компоновки Данных (СКД) часто возникает задача вывести список сотрудников вместе с их контактными данными: телефонами, адресами электронной почты и т.д. Однако при простом соединении кадровых данных с регистром контактной информации можно столкнуться с неприятной проблемой — дублированием строк. Разберем, почему это происходит и как это исправить на примере отчета в 1С:Бухгалтерия 3.0.
Проанализируем исходную ситуацию. Есть запрос, который получает последние кадровые сведения о сотрудниках и соединяется с таблицей контактной информации физических лиц.
Исходный запрос с проблемой:
ВЫБРАТЬ
КадроваяИсторияСотрудниковСрезПоследних.ФизическоеЛицо,
КадроваяИсторияСотрудниковСрезПоследних.Организация,
КадроваяИсторияСотрудниковСрезПоследних.Подразделение,
КадроваяИсторияСотрудниковСрезПоследних.Должность,
КадроваяИсторияСотрудниковСрезПоследних.ФизическоеЛицо.ДатаРождения,
ФизическиеЛицаКонтактнаяИнформация.АдресЭП,
ФизическиеЛицаКонтактнаяИнформация.НомерТелефона
ИЗ
РегистрСведений.КадроваяИсторияСотрудников.СрезПоследних(&НаДату, ) КАК КадроваяИсторияСотрудниковСрезПоследних
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ФизическиеЛица.КонтактнаяИнформация КАК ФизическиеЛицаКонтактнаяИнформация
ПО КадроваяИсторияСотрудниковСрезПоследних.ФизическоеЛицо = ФизическиеЛицаКонтактнаяИнформация.Ссылка
Причина дублей кроется в ЛЕВОЕ СОЕДИНЕНИЕ с табличной частью Справочник.ФизическиеЛица.КонтактнаяИнформация (или с регистром сведений КонтактнаяИнформация). Если у одного физического лица в системе заведено несколько видов контактов (например, рабочий телефон, личный телефон, email), то для каждой записи в таблице контактов будет создана отдельная строка в итоговом результате. В результате мы видим одного и того же сотрудника несколько раз.
Рассмотрим два основных способа решения этой проблемы.
Этот подход заключается в том, чтобы в основном запросе не соединяться с таблицей контактов, а получить их позже, на этапе вывода данных в СКД, для каждой строки отчета. Это позволяет избежать дублей на уровне запроса.
Шаг 1. Упрощаем основной запрос
Сначала уберем из запроса соединение с контактной информацией. Наш основной набор данных будет содержать уникальный список сотрудников без дублей.
ВЫБРАТЬ
КадроваяИсторияСотрудниковСрезПоследних.ФизическоеЛицо,
КадроваяИсторияСотрудниковСрезПоследних.Организация,
КадроваяИсторияСотрудниковСрезПоследних.Подразделение,
КадроваяИсторияСотрудниковСрезПоследних.Должность,
КадроваяИсторияСотрудниковСрезПоследних.ФизическоеЛицо.ДатаРождения
ИЗ
РегистрСведений.КадроваяИсторияСотрудников.СрезПоследних(&НаДату, ) КАК КадроваяИсторияСотрудниковСрезПоследних
Шаг 2. Создаем вычисляемое поле в СКД
Теперь в схеме компоновки данных на вкладке "Вычисляемые поля" добавим новое поле, например, "ЭлектроннаяПочта". В колонке "Выражение" нужно указать, как его рассчитать. Первая мысль — использовать стандартную функцию для получения контактов:
УправлениеКонтактнойИнформацией.КонтактнаяИнформацияОбъекта(ФизическоеЛицо, Справочники.ВидыКонтактнойИнформации.EMailФизическиеЛица, ТекущаяДата())
Однако при попытке выполнить отчет мы получим ошибку "Поле не найдено: Справочники".
Важный момент: язык выражений СКД работает в ограниченном контексте. Он не имеет прямого доступа к объектам конфигурации, таким как Справочники, Перечисления и т.д., на клиенте. Эти выражения выполняются в специальной изолированной среде.
Шаг 3. Создаем "функцию-обертку" в общем модуле
Чтобы обойти это ограничение, создадим свою функцию в общем модуле. Эта функция будет выполняться на сервере, где есть доступ ко всем объектам конфигурации.
мМоиОтчеты).
Функция ПолучитьЭлектроннуюПочту(ФизЛицо) Экспорт
// Получаем предопределенный элемент справочника ВидыКонтактнойИнформации
// Название предопределенного элемента может отличаться в разных конфигурациях.
// Проверьте его точное имя в вашей базе.
ВидКИ = Справочники.ВидыКонтактнойИнформации.EMailФизическиеЛица;
// Вызываем стандартную функцию БСП
ПредставлениеКонтакта = УправлениеКонтактнойИнформацией.КонтактнаяИнформацияОбъекта(ФизЛицо, ВидКИ);
Возврат ПредставлениеКонтакта;
КонецФункции
Шаг 4. Указываем вызов нашей функции в вычисляемом поле
Теперь вернемся в СКД и в выражении для нашего вычисляемого поля "ЭлектроннаяПочта" напишем вызов созданной нами функции:
мМоиОтчеты.ПолучитьЭлектроннуюПочту(ФизическоеЛицо)
Таким же образом можно создать функции для получения телефона и других видов контактной информации. Этот метод надежен и решает проблему, но может быть не самым производительным на очень больших объемах данных, так как функция будет вызываться для каждой строки отчета.
Этот способ является более производительным, так как вся обработка данных происходит на уровне СУБД в рамках одного запроса. Идея состоит в том, чтобы присоединять таблицу с контактами не один раз "в общем", а несколько раз — по одному для каждого нужного вида контакта.
Шаг 1. Используем несколько левых соединений
Мы будем использовать ЛЕВОЕ СОЕДИНЕНИЕ с регистром сведений КонтактнаяИнформация для каждого типа контакта (одно соединение для email, второе для телефона и т.д.). В условии соединения мы явно укажем, какой вид информации нас интересует.
Пример усовершенствованного запроса:
ВЫБРАТЬ
КадроваяИстория.ФизическоеЛицо,
КадроваяИстория.Организация,
КадроваяИстория.Подразделение,
КадроваяИстория.Должность,
КадроваяИстория.ФизическоеЛицо.ДатаРождения,
ЕСТЬNULL(ЭлектроннаяПочта.Представление, "") КАК АдресЭП,
ЕСТЬNULL(Телефон.Представление, "") КАК НомерТелефона
ИЗ
РегистрСведений.КадроваяИсторияСотрудников.СрезПоследних(&НаДату, ) КАК КадроваяИстория
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КонтактнаяИнформация КАК ЭлектроннаяПочта
ПО КадроваяИстория.ФизическоеЛицо = ЭлектроннаяПочта.Объект
И (ЭлектроннаяПочта.Вид = &ВидЭлектроннаяПочта)
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КонтактнаяИнформация КАК Телефон
ПО КадроваяИстория.ФизическоеЛицо = Телефон.Объект
И (Телефон.Вид = &ВидТелефон)
Шаг 2. Настраиваем параметры в СКД
В этом запросе появились два новых параметра: &ВидЭлектроннаяПочта и &ВидТелефон. Их необходимо настроить в СКД:
&ВидЭлектроннаяПочта в колонке "Выражение" укажите путь к предопределенному элементу справочника. Для этого нажмите кнопку выбора, перейдите в Справочники -> ВидыКонтактнойИнформации -> Предопределенные и выберите нужный элемент (например, EMailФизическиеЛица).&ВидТелефон, выбрав соответствующий вид контакта.Функция ЕСТЬNULL(..., "") используется для того, чтобы в отчете выводилась пустая строка, а не значение NULL, если у сотрудника отсутствует тот или иной вид контакта.
Часто в отчетах необходимо видеть только актуальных, работающих сотрудников. Виртуальная таблица СрезПоследних возвращает последнее состояние сотрудника, которое может быть и "Уволен". Чтобы исключить уволенных, добавьте в запрос условие ГДЕ.
ВЫБРАТЬ
...
ИЗ
РегистрСведений.КадроваяИсторияСотрудников.СрезПоследних(&НаДату, ) КАК КадроваяИстория
...
ГДЕ
КадроваяИстория.ПричинаИзмененияСостояния <> ЗНАЧЕНИЕ(Перечисление.ПричиныИзмененияСостояния.Увольнение)
Таким образом, вы получите отчет только по сотрудникам, которые числятся работающими на указанную дату.