Как найти позицию штатного расписания по табельному номеру и записать данные в реквизит

Программист 1С v8.3 (Управляемые формы) 1С:Зарплата и Управление Персоналом IT и автоматизация бизнеса
← На главную

В процессе разработки на платформе 1С:Предприятие 8.3, особенно в конфигурации «Зарплата и управление персоналом 3.1», часто возникает задача пакетного обновления данных в справочниках. Рассмотрим типичную ситуацию: у нас есть массив данных с табельными номерами и некими ключами, которые нужно записать в новый реквизит справочника ШтатноеРасписание. Проанализируем ошибки, которые часто совершают начинающие разработчики, и выработаем оптимальное решение.

Разбор основных ошибок в подходе

Для начала разберем исходный код и выясним, почему он не будет работать и какие архитектурные проблемы в нем заложены. Основная ошибка заключается в обращении к метаданным вместо конкретных экземпляров данных. Рассмотрим строку ШтатРаспОбъект = ШтатноеРасп.ПолучитьОбъект(). Здесь ШтатноеРасп — это менеджер справочника (Справочники.ШтатноеРасписание). У менеджера нет метода ПолучитьОбъект() без параметров, так как он управляет всем классом объектов, а не конкретным элементом. Чтобы изменить данные, нам нужно сначала получить Ссылку на конкретный элемент, а уже у ссылки вызвать метод ПолучитьОбъект().

Второй критический момент — запрос внутри цикла Для Каждого. Это считается «антипаттерном» в разработке на 1С. Выполнение запроса к базе данных на каждой итерации цикла создает колоссальную нагрузку на сервер СУБД. Если в вашем массиве будет 1000 сотрудников, система выполнит 1000 отдельных обращений к серверу. Правильный подход — собрать все необходимые данные одним запросом.

Третья ошибка — вызов метода ШтатноеРасп.Записать(). Как и в случае с получением объекта, метод Записать() вызывается у конкретного объекта (экземпляра данных), а не у менеджера справочника.

Оптимизация: Подготовка данных и работа с запросом

Вместо того чтобы «мучить» базу данных постоянными мелкими запросами, подготовим массив табельных номеров и передадим его в запрос целиком. Проанализируем структуру регистра сведений ЗанятостьПозицийШтатногоРасписания. В нем хранится связь между сотрудниками и их позициями. Нам необходимо получить срез последних данных, чтобы найти актуальную позицию для каждого кода сотрудника.

Важный нюанс: Табельный номер в 1С часто хранится в реквизите Код справочника Сотрудники. Однако коды могут иметь ведущие нули (например, "000012"). Если ваш входящий массив содержит числа, поиск может не сработать. Перед выполнением запроса убедитесь, что форматы данных совпадают.

Пошаговый алгоритм правильного решения

  1. Подготовка параметров: Выгружаем все табельные номера из входящего массива в отдельный список или массив для передачи в запрос.
  2. Написание запроса: Используем оператор В (&МассивКодов) в условии ГДЕ. Это позволит получить все соответствия за один раз.
  3. Обработка результата: Обходим выборку запроса и для каждой найденной позиции получаем объект.
  4. Запись данных: Присваиваем значение реквизиту и записываем объект. Для ускорения процесса и обеспечения целостности данных рекомендуется использовать транзакции.

Реализация кода с учетом стандартов разработки

Рассмотрим подробнее, как должен выглядеть качественный программный код для решения этой задачи. Проанализируем использование временных таблиц или простых параметров массива.


// 1. Подготовим массив кодов из исходных данных (ДанныеМ)
МассивКодов = Новый Массив;
Для Каждого СтрокаДанных Из ДанныеМ Цикл
    МассивКодов.Добавить(СтрокаДанных[0]); // Предполагаем, что индекс 0 - это табельный номер
КонецЦикла;

// 2. Формируем запрос для получения всех ссылок сразу
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ РАЗЛИЧНЫЕ
    |	Занятость.Сотрудник.Код КАК ТабельныйНомер,
    |	Занятость.ПозицияШтатногоРасписания КАК СсылкаНаПозицию
    |ИЗ
    |	РегистрСведений.ЗанятостьПозицийШтатногоРасписания.СрезПоследних(&Период, Сотрудник.Код В (&МассивКодов)) КАК Занятость
    |ГДЕ
    |	Занятость.ВидЗанятостиПозиции <> ЗНАЧЕНИЕ(Перечисление.ВидыЗанятостиПозицийШтатногоРасписания.Свободна)";

Запрос.УстановитьПараметр("Период", ТекущаяДата());
Запрос.УстановитьПараметр("МассивКодов", МассивКодов);

РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();

// 3. Открываем транзакцию для безопасной и быстрой записи
НачатьТранзакцию();

Попытка
    Пока Выборка.Следующий() Цикл
        // Находим соответствующий ключ в исходном массиве (для упрощения используем поиск в цикле, 
        // но в идеале лучше использовать Соответствие)
        ТекущийКлюч = "";
        Для Каждого М Из ДанныеМ Цикл
            Если М[0] = Выборка.ТабельныйНомер Тогда
                ТекущийКлюч = М[1];
                Прервать;
            КонецЕсли;
        КонецЦикла;
        
        // Получаем объект из ссылки
        ПозицияОбъект = Выборка.СсылкаНаПозицию.ПолучитьОбъект();
        
        Если ПозицияОбъект <> Неопределено Тогда
            // Записываем значение в новый реквизит (добавленный через расширение)
            ПозицияОбъект.Ключ = ТекущийКлюч;
            
            // Отключаем лишние проверки, если реквизит технический
            ПозицияОбъект.ОбменДанными.Загрузка = Истина;
            
            ПозицияОбъект.Записать();
        КонецЕсли;
    КонецЦикла;
    
    ЗафиксироватьТранзакцию();
Исключение
    ОтменитьТранзакцию();
    ЗаписьЖурналаРегистрации("Ошибка записи в штатное расписание", УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки());
    ВызватьИсключение;
КонецПопытки;

Особенности работы со справочником «Штатное расписание»

Выясним причину, по которой прямое изменение объекта справочника ШтатноеРасписание может быть затруднительным. В ЗУП 3.1 ведение штатного расписания часто настроено с сохранением истории изменений. Если в настройках программы установлен флаг «Ведется история изменений штатного расписания», то любое изменение должно фиксироваться документом «Изменение штатного расписания».

Однако, если ваш реквизит Ключ является чисто техническим и не влияет на кадровый учет (например, идентификатор для внешней интеграции), использование ОбменДанными.Загрузка = Истина позволит обойти бизнес-логику проверки истории и записать данные напрямую в элемент. Рассмотрим этот момент подробнее: использование этого флага подавляет события ПередЗаписью и ПриЗаписи в модуле объекта, что предотвращает нежелательные проверки соответствия ФОТ или графиков работы.

Помните: Если сотрудник занимает несколько позиций (внутреннее совместительство), запрос вернет несколько строк для одного табельного номера. Проанализируйте ситуацию: нужно ли записывать ключ во все позиции сотрудника или только в основную. Если только в основную, добавьте в запрос условие по полю ВидЗанятости (Основное место работы).

Использование «Соответствия» для сопоставления данных

Чтобы избежать вложенного цикла при поиске ключа для конкретной ссылки, воспользуемся объектом Соответствие. Это значительно ускорит работу кода при больших объемах данных.


// Создаем соответствие для быстрого поиска: ТабельныйНомер -> Ключ
КартаКлючей = Новый Соответствие;
Для Каждого М Из ДанныеМ Цикл
    КартаКлючей.Вставить(М[0], М[1]);
КонецЦикла;

// Теперь в цикле выборки поиск будет мгновенным
Пока Выборка.Следующий() Цикл
    НужныйКлюч = КартаКлючей.Получить(Выборка.ТабельныйНомер);
    Если НужныйКлюч <> Неопределено Тогда
        // ... логика записи объекта ...
    КонецЕсли;
КонецЦикла;

Подведем итог: для успешного решения задачи необходимо соблюдать иерархию объектов 1С (Менеджер -> Ссылка -> Объект), минимизировать количество обращений к серверу СУБД путем использования пакетных запросов и обеспечивать безопасность записи через транзакции и обработку исключений. Такой подход гарантирует стабильную работу системы даже при значительном росте объема данных.

← На главную