При разработке отчетов и обработок в среде 1С:Предприятие 8 часто возникает задача дополнения уже имеющейся таблицы значений (ТЗ) данными из типовых механизмов конфигурации. Одной из таких задач является получение актуального состояния сотрудника (работает, уволен, в отпуске и т.д.). Рассмотрим подробно, как правильно загрузить данные, получить кадровые показатели и объединить их в итоговую таблицу, используя исключительно программный код.
Часто в консолях запросов или специальных обработках промежуточные результаты сохраняются в файлы с расширением .VT_. Это сериализованные объекты типа ТаблицаЗначений. Для того чтобы начать работу с ними в коде, нам необходимо восстановить объект из файла. Разберем типичную ошибку начинающих: использование метода ЗагрузитьИзФайла(). Такого метода у объекта ТаблицаЗначений не существует в стандартном глобальном контексте для этого расширения. Вместо этого мы должны использовать глобальную функцию ЗначениеИзФайла().
Проанализируем пример загрузки:
// Указываем путь к вашему файлу
ПутьКФайлу = "C:\temp\тз1.VT_";
// Восстанавливаем таблицу значений
ТаблицаСотрудников = ЗначениеИзФайла(ПутьКФайлу);
Если ТипЗнч(ТаблицаСотрудников) <> Тип("ТаблицаЗначений") Тогда
Сообщить("Не удалось загрузить таблицу или файл поврежден!");
КонецЕсли;
Важно помнить, что если вы загружаете данные, полученные из другой информационной базы (например, из копии), ссылки в колонке Ссылка могут оказаться "битыми" (объект не найден), так как УИДы (уникальные идентификаторы) объектов в разных базах различаются.
Для получения состояния сотрудников в типовых конфигурациях (ЗУП 3.1, ERP 2.5, КА 2.5) используется универсальный механизм в общем модуле КадровыйУчет. Функция КадровыеДанныеСотрудников требует на вход массив ссылок на сотрудников. Мы можем получить его быстро, используя метод таблицы ВыгрузитьКолонку().
Рассмотрим этот этап по шагам:
// 1. Формируем массив сотрудников из загруженной ТЗ
МассивСотрудников = ТаблицаСотрудников.ВыгрузитьКолонку("Ссылка");
// 2. Указываем дату, на которую нам нужно состояние
ДатаАктуальности = ТекущаяДата();
// 3. Вызываем функцию получения данных.
// Первый параметр "Истина" означает, что нам нужны данные только по работающим (или актуальные записи)
// Третий параметр - список полей через запятую
ТаблицаСостояний = КадровыйУчет.КадровыеДанныеСотрудников(Истина, МассивСотрудников, "Состояние", ДатаАктуальности);
Обратите внимание, что функция КадровыеДанныеСотрудников не изменяет исходную таблицу, а возвращает новую таблицу значений, в которой обязательно будет колонка Сотрудник (или Ссылка) и запрошенные вами поля (в данном случае Состояние).
Даже если стоит задача выполнить работу "в коде", это не запрещает нам использовать объект Запрос. Это наиболее эффективный и быстрый способ объединения двух таблиц значений. Рассмотрим, как "загнать" наши таблицы во временные таблицы запроса.
Запрос = Новый Запрос;
// Передаем наши таблицы в параметры запроса
Запрос.Параметры.Вставить("ТаблицаСотрудников", ТаблицаСотрудников);
Запрос.Параметры.Вставить("ТаблицаСостояний", ТаблицаСостояний);
Запрос.Текст =
"ВЫБРАТЬ
| ТЗ_Сотр.Ссылка КАК Сотрудник,
| ТЗ_Сотр.Подразделение КАК Подразделение,
| ЕСТЬNULL(ТЗ_Сост.Состояние, ""Не определено"") КАК Состояние
|ПОМЕСТИТЬ втДанные
|ИЗ
| &ТаблицаСотрудников КАК ТЗ_Сотр
| ЛЕВОЕ СОЕДИНЕНИЕ &ТаблицаСостояний КАК ТЗ_Сост
| ПО ТЗ_Сотр.Ссылка = ТЗ_Сост.Сотрудник
|;
|
|ВЫБРАТЬ * ИЗ втДанные";
РезультатТЗ = Запрос.Выполнить().Выгрузить();
Этот метод хорош тем, что платформа 1С самостоятельно оптимизирует соединение данных, и вам не нужно заботиться о ручном поиске соответствий.
Если по каким-то причинам использование запроса невозможно, разберем алгоритм ручного объединения. Чтобы процедура не работала медленно на больших массивах данных (например, 10 000 сотрудников), необходимо использовать индексацию.
Проанализируем последовательность действий:
ТаблицаСотрудников.Колонки.Добавить("Состояние");.ТаблицаСостояний.Индексы.Добавить("Сотрудник");.Пример реализации кода:
// Подготовка
ТаблицаСотрудников.Колонки.Добавить("Состояние");
ТаблицаСостояний.Индексы.Добавить("Сотрудник");
// Обход и заполнение
Для Каждого СтрокаОсновная Из ТаблицаСотрудников Цикл
// Ищем строку во второй таблице по ключу "Сотрудник"
ПараметрыПоиска = Новый Структура("Сотрудник", СтрокаОсновная.Ссылка);
НайденныеСтроки = ТаблицаСостояний.НайтиСтроки(ПараметрыПоиска);
Если НайденныеСтроки.Количество() > 0 Тогда
// Берем состояние из первой найденной строки
СтрокаОсновная.Состояние = НайденныеСтроки[0].Состояние;
КонецЕсли;
КонецЦикла;
Рассмотрим несколько моментов, которые помогут избежать ошибок при реализации:
Типизация даты: Функция КадровыеДанныеСотрудников очень чувствительна к формату даты. Всегда убеждайтесь, что ДатаАктуальности — это объект типа Дата, а не строка. Если вы хотите получить данные на текущий момент времени, используйте ТекущаяДатаСеанса(), чтобы избежать расхождений с серверным временем.
Индексация: Никогда не пренебрегайте индексами при использовании метода НайтиСтроки() в циклах. Без индекса поиск выполняется перебором (сложность O(n)), что при двух таблицах по 1000 строк даст миллион операций сравнения. С индексом скорость возрастает в десятки раз.
Использование ЗаполнитьЗначенияСвойств: Если из таблицы состояний нужно перенести не одну колонку, а несколько (например, Состояние, Должность, Подразделение), удобно использовать метод ЗаполнитьЗначенияСвойств(СтрокаПриемник, СтрокаИсточник). Это избавит вас от написания множества строк присваивания.
Выясним причину, по которой иногда КадровыеДанныеСотрудников возвращает пустые значения. Чаще всего это происходит, когда массив сотрудников содержит ссылки, помеченные на удаление или не существующие в регистрах кадровых сведений на указанную дату (например, сотрудник еще не принят на работу на эту дату).
В завершение отметим, что для обучения "в консоли кода" полезно приучать себя к правильному форматированию ("лесенке"). Это значительно упрощает чтение вложенных циклов и условий, как мы видели в примерах выше.