При разработке отчетов, печатных форм или кастомных обработок в среде «1С:Зарплата и управление персоналом 3.1» (а также в ERP 2 и Комплексной автоматизации 2) программисты регулярно сталкиваются с необходимостью получения персональных данных сотрудников и физических лиц на определенные даты. Прямые запросы к регистрам сведений в современных конфигурациях не приветствуются разработчиками фирмы «1С». Вместо этого стандартом разработки является использование программного интерфейса (API) общего модуля КадровыйУчет.
Одним из самых мощных инструментов этого модуля является механизм генерации временных таблиц с помощью процедур СоздатьВТКадровыеДанныеСотрудников и СоздатьВТКадровыеДанныеФизическихЛиц. Однако из-за многоэтапности построения таких запросов легко запутаться в именах исходных и результирующих временных таблиц. В этой статье мы подробно разберем типичную ошибку «Поле не найдено ВТКадровыеДанныеФизическихЛиц.ФИОПолные», выясним причину ее возникновения и рассмотрим пошаговое решение проблемы.
Рассмотрим ситуацию, когда разработчик пытается сформировать пакетный запрос для получения информации по кадровым переводам (как одиночным, так и списочным). Для этого в первом запросе пакета собираются данные по сотрудникам и периодам перевода во временную таблицу ВТСотрудникиПериоды. Из нее же формируется еще одна вспомогательная таблица — ВТФизическиеЛицаПериоды, предназначенная для получения личных данных руководителя (его ФИО):
ВЫБРАТЬ
ВТСотрудникиПериоды.Ссылка КАК Ссылка,
ВТСотрудникиПериоды.Период КАК Период,
ВТСотрудникиПериоды.Руководитель КАК ФизическоеЛицо
ПОМЕСТИТЬ ВТФизическиеЛицаПериоды
ИЗ
ВТСотрудникиПериоды КАК ВТСотрудникиПериоды
После выполнения первой части запроса программист вызывает методы общего модуля КадровыйУчет для автоматического расчета и заполнения таблиц с кадровыми данными сотрудников и физических лиц:
ОписательВременныхТаблиц = КадровыйУчет.ОписательВременныхТаблицДляСоздатьВТКадровыеДанныеФизическихЛиц(Запрос.МенеджерВременныхТаблиц, "ВТФизическиеЛицаПериоды");
ОписательВременныхТаблиц.Вставить("ИмяВТКадровыеДанныеФизическихЛиц", "ВТКадровыеДанныеФизическихЛиц");
КадровыйУчет.СоздатьВТКадровыеДанныеФизическихЛиц(ОписательВременныхТаблиц, Истина, "ФИОПолные,ФамилияИО,Пол,ИОФамилия");
Затем во втором запросе пакета разработчик пытается выбрать поля ФИО руководителя из результирующей таблицы ВТКадровыеДанныеФизическихЛиц:
ВЫБРАТЬ
...
ВТКадровыеДанныеФизическихЛиц.ФИОПолные КАК РуководительФИОПолные,
ВТКадровыеДанныеФизическихЛиц.ФамилияИО КАК РуководительФамилияИО,
...
ИЗ
ВТСотрудникиПериоды КАК ВТСотрудникиПериоды
...
ЛЕВОЕ СОЕДИНЕНИЕ ВТФизическиеЛицаПериоды КАК ВТФизическиеЛицаПериоды
ПО ВТСотрудникиПериоды.Руководитель = ВТФизическиеЛицаПериоды.ФизическоеЛицо
И (ВТСотрудникиПериоды.Период = ВТФизическиеЛицаПериоды.Период)
При выполнении этого кода система выдает ошибку компиляции запроса:
Ошибка Поле не найдено "ВТКадровыеДанныеФизическихЛиц.ФИОПолные"
Проанализируем структуру связей во втором запросе. Внимательно посмотрите на секцию ИЗ и последующие соединения ЛЕВОЕ СОЕДИНЕНИЕ. Разработчик обращается к полям таблицы ВТКадровыеДанныеФизическихЛиц в секции ВЫБРАТЬ, но в секции связи (ПО/ИЗ) подключает совершенно другую таблицу — ВТФизическиеЛицаПериоды!
В этом и кроется главная логическая ошибка:
ВТФизическиеЛицаПериоды — это лишь «затравочная» (исходная) таблица. В ней содержатся только три базовых поля: Ссылка, Период и ФизическоеЛицо. Никаких полей ФИО или паспортных данных там физически нет.КадровыйУчет.СоздатьВТКадровыеДанныеФизическихЛиц на основании этой «затравки» формирует в менеджере временных таблиц новую результирующую таблицу с именем ВТКадровыеДанныеФизическихЛиц. Именно в этой новой таблице появляются колонки ФИОПолные, ФамилияИО и другие запрашиваемые данные.ФИОПолные в таблице ВТКадровыеДанныеФизическихЛиц, которая вообще не была объявлена в блоке ИЗ / ЛЕВОЕ СОЕДИНЕНИЕ текущего запроса, что и приводит к ошибке.Чтобы исправить ошибку, нам необходимо выполнить правильное левое соединение именно с результирующей временной таблицей, которую сгенерировал для нас общий модуль кадрового учета.
Шаг 1. Корректируем блок соединений в запросе.
Вместо левого соединения с таблицей-источником ВТФизическиеЛицаПериоды мы должны использовать таблицу результатов ВТКадровыеДанныеФизическихЛиц. Изменим соединение следующим образом:
ЛЕВОЕ СОЕДИНЕНИЕ ВТКадровыеДанныеФизическихЛиц КАК ВТКадровыеДанныеФизическихЛиц
ПО ВТСотрудникиПериоды.Руководитель = ВТКадровыеДанныеФизическихЛиц.ФизическоеЛицо
И (ВТСотрудникиПериоды.Период = ВТКадровыеДанныеФизическихЛиц.Период)
Обратите внимание на условия связи. Мы связываем руководителя (который имеет тип СправочникСсылка.ФизическиеЛица) с полем ФизическоеЛицо результирующей таблицы кадровых данных на нужную дату (Период).
Шаг 2. Оптимизируем код создания описателя временных таблиц.
В коде создания описателя присутствует следующая строка:
ОписательВременныхТаблиц.Вставить("ИмяВТКадровыеДанныеФизическихЛиц", "ВТКадровыеДанныеФизическихЛиц");
Данная строка является избыточной, поскольку по умолчанию метод СоздатьВТКадровыеДанныеФизическихЛиц и так называет результирующую таблицу именем ВТКадровыеДанныеФизическихЛиц. Переопределять это свойство структуры имеет смысл только тогда, когда вам в рамках одного алгоритма требуется получить данные физлиц несколько раз (например, на разные даты или для разных категорий сотрудников) и избежать конфликта имен в менеджере временных таблиц.
Давайте соберем правильный и работающий пакетный запрос с учетом всех исправлений. Код формирования и выполнения запросов будет выглядеть так:
// Инициализируем запрос и передаем параметры
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
Запрос.УстановитьПараметр("МассивСсылка", МассивОбъектов);
Запрос.Текст =
"ВЫБРАТЬ
| КадровыйПеревод.Ссылка КАК Ссылка,
| КадровыйПеревод.Сотрудник КАК Сотрудник,
| КадровыйПеревод.ДатаНачала КАК Период,
| КадровыйПеревод.Руководитель КАК Руководитель,
| КадровыйПеревод.Организация КАК Организация,
| КадровыйПеревод.ДатаОкончания КАК ДатаОкончания
|ПОМЕСТИТЬ ВТСотрудникиПериоды
|ИЗ
| Документ.КадровыйПеревод КАК КадровыйПеревод
|ГДЕ
| КадровыйПеревод.Ссылка В(&МассивСсылка)
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ
| КадровыйПереводСпискомСотрудники.Ссылка,
| КадровыйПереводСпискомСотрудники.Сотрудник,
| КадровыйПереводСпискомСотрудники.ДатаНачала,
| КадровыйПереводСпискомСотрудники.Ссылка.Руководитель,
| КадровыйПереводСпискомСотрудники.Ссылка.Организация,
| КадровыйПереводСпискомСотрудники.ДатаОкончания
|ИЗ
| Документ.КадровыйПереводСписком.Сотрудники КАК КадровыйПереводСпискомСотрудники
|ГДЕ
| КадровыйПереводСпискомСотрудники.Ссылка В(&МассивСсылка)
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ВТСотрудникиПериоды.Ссылка КАК Ссылка,
| ВТСотрудникиПериоды.Период КАК Период,
| ВТСотрудникиПериоды.Руководитель КАК ФизическоеЛицо
|ПОМЕСТИТЬ ВТФизическиеЛицаПериоды
|ИЗ
| ВТСотрудникиПериоды КАК ВТСотрудникиПериоды";
Запрос.Выполнить();
// Генерация кадровых данных физических лиц (наших руководителей)
ОписательВременныхТаблиц = КадровыйУчет.ОписательВременныхТаблицДляСоздатьВТКадровыеДанныеФизическихЛиц(Запрос.MенеджерВременныхТаблиц, "ВТФизическиеЛицаПериоды");
КадровыйУчет.СоздатьВТКадровыеДанныеФизическихЛиц(ОписательВременныхТаблиц, Истина, "ФИОПолные,ФамилияИО,Пол,ИОФамилия");
// Выполняем финальный запрос с получением всех необходимых ФИО
Запрос.Текст =
"ВЫБРАТЬ
| ВТСотрудникиПериоды.Ссылка КАК Ссылка,
| ВТСотрудникиПериоды.Сотрудник КАК Сотрудник,
| ВТСотрудникиПериоды.Период КАК Период,
| ВТСотрудникиПериоды.Руководитель КАК Руководитель,
| ВТКадровыеДанныеФизическихЛиц.ФИОПолные КАК РуководительФИОПолные,
| ВТКадровыеДанныеФизическихЛиц.ФамилияИО КАК РуководительФамилияИО,
| ВТКадровыеДанныеФизическихЛиц.ИОФамилия КАК РуководительИОФамилия,
| ВТКадровыеДанныеФизическихЛиц.Пол КАК РуководительПол
|ИЗ
| ВТСотрудникиПериоды КАК ВТСотрудникиПериоды
| // Важное исправление: соединяем с результирующей ВТКадровыеДанныеФизическихЛиц!
| ЛЕВОЕ СОЕДИНЕНИЕ ВТКадровыеДанныеФизическихЛиц КАК ВТКадровыеДанныеФизическихЛиц
| ПО ВТСотрудникиПериоды.Руководитель = ВТКадровыеДанныеФизическихЛиц.ФизическоеЛицо
| И (ВТСотрудникиПериоды.Период = ВТКадровыеДанныеФизическихЛиц.Период)
|УПОРЯДОЧИТЬ ПО
| Ссылка,
| Сотрудник";
РезультатЗапроса = Запрос.Выполнить();
Проанализируем архитектуру этого решения. Если перед вами стоит задача быстро получить данные нескольких физических лиц или сотрудников без написания тяжелых многоуровневых запросов к базе данных, использование механизма временных таблиц может оказаться избыточным усложнением кода.
Для простых операций в программном интерфейсе КадровыйУчет предусмотрены гораздо более простые функции-обертки, возвращающие стандартную ТаблицуЗначений. Рассмотрим, как можно решить эту задачу альтернативным путем:
// Предположим, у нас есть массив физических лиц (руководителей)
МассивФизическихЛиц = Новый Массив;
МассивФизическихЛиц.Добавить(СсылкаНаРуководителя);
// Определяем список необходимых нам полей через запятую
КадровыеДанные = "ФИОПолные,ФамилияИО,Пол,ИОФамилия";
// Получаем таблицу значений с готовыми данными на конкретную дату
ТаблицаДанныхРуководителей = КадровыйУчет.КадровыеДанныеФизическихЛиц(Истина, МассивФизическихЛиц, КадровыеДанные, ТекущаяДата());
// Теперь мы можем легко найти нужную строку и прочитать значения
Для Каждого СтрокаДанных Из ТаблицаДанныхРуководителей Цикл
Сообщить(СтрокаДанных.ФИОПолные);
КонецЦикла;
Использование функции КадровыйУчет.КадровыеДанныеФизическихЛиц() позволяет существенно повысить читаемость вашего кода, минимизировать вероятность совершения ошибок при соединении таблиц в запросах, а также облегчает последующую поддержку конфигурации при обновлениях.