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