Как программно заполнить новый элемент справочника данными из запроса: разбираем ошибки на примере "Рабочих мест охраны труда"

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

Часто в практике разработки на 1С возникает задача программного создания элементов одного справочника на основании данных другого. Начинающие разработчики нередко сталкиваются с рядом неочевидных проблем при попытке перенести значения реквизитов. Давайте разберем типичную ситуацию: необходимо создать новый элемент справочника РабочиеМестаОхраныТруда, заполнив его данными из справочника ШтатноеРасписание.

Рассмотрим код, который на первый взгляд кажется логичным, но при выполнении выдает ошибку: «Значение поля "Организация" не заполнено или заполнено неверно».

Анализ исходного кода и выявление ошибок

Проанализируем функцию, которая создает новый элемент:


&НаСервере
Функция ЗаполнитьРМ(ТаблицаЗначенийПозицииШР) Экспорт
    РабМесто = Справочники.РабочиеМестаОхраныТруда.СоздатьЭлемент;
    РеквизитыРМ = РабМесто.Метаданные.Реквизиты;
    Попытка
        ЗаполнитьЗначенияСвойств(РабМесто, ТаблицаЗначенийПозицииШР, РеквизитыРМ);
        Результат = Истина;
        РабМесто.Записать;
    Исключение
        Результат = Ложь;
    КонецПопытки;
    Возврат Результат;
КонецФункции

В эту функцию передается переменная ТаблицаЗначенийПозицииШР, полученная ранее методом Запрос.Выполнить().Выгрузить(). Разберем по шагам, почему этот подход не работает.

Ошибка №1: Передача Таблицы Значений в метод ЗаполнитьЗначенияСвойств

Самая критичная ошибка в данном коде кроется в строке ЗаполнитьЗначенияСвойств(РабМесто, ТаблицаЗначенийПозицииШР...).

Функция ЗаполнитьЗначенияСвойств предназначена для копирования значений свойств с одинаковыми именами из объекта-источника в объект-приемник. Однако мы передаем в качестве источника объект типа ТаблицаЗначений. Сама по себе таблица значений не имеет свойств, соответствующих колонкам (например, у нее нет свойства "Наименование" или "Подразделение"). В результате функция ничего не находит, и ни один реквизит нового элемента РабМесто не заполняется. Элемент остается абсолютно пустым.

Решение: В метод необходимо передавать конкретную строку таблицы или результат выборки из запроса. Если мы используем выгрузку в таблицу, код должен выглядеть так: ЗаполнитьЗначенияСвойств(РабМесто, ТаблицаЗначенийПозицииШР[0]).

Однако более правильным и оптимальным подходом в 1С является использование выборки из запроса, минуя выгрузку в таблицу:


Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
    ЗаполнитьЗначенияСвойств(РабМесто, Выборка);
КонецЕсли;

Ошибка №2: Ошибка "Организация не заполнена" при отсутствии такого реквизита

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

Дело в том, что справочник имеет владельца (справочник Организации). В пользовательском режиме и на формах стандартное поле Владелец часто переименовывается в "Организация". В 1С Владелец является обязательным для заполнения полем.

Кроме того, в современных конфигурациях справочник рабочих мест иерархический. В процедуре ПередЗаписью модуля объекта часто зашит алгоритм: если элемент записывается, система проверяет наличие родительской папки (например, с именем подразделения) и, если ее нет, пытается ее создать программно (вызывается метод СоздатьГруппуРабочегоМеста). Так как мы не заполнили Владельца у нашего нового пустого элемента, система не знает, к какой организации привязать создаваемую родительскую папку, и выдает ошибку при записи группы.

Решение: Необходимо явно получать Владельца (Организацию) из Штатного расписания и заполнять его. Элементы Штатного расписания обычно привязаны к Подразделению, а уже Подразделение имеет реквизит Владелец или Организация. Запрос нужно дополнить получением этого поля:


// В тексте запроса добавляем получение организации
// ...
ШтатноеРасписание.Подразделение.Владелец КАК ВладелецОрганизация,
// ...

А при заполнении элемента явно присвоить значение:

РабМесто.Владелец = Выборка.ВладелецОрганизация;

Ошибка №3: Избыточность запроса

В исходном коде автор запрашивает абсолютно все реквизиты и вложенные табличные части из справочника Штатного расписания (начисления, показатели, отпуска и т.д.). Для создания карточки рабочего места охраны труда 90% этих данных не нужны.

Решение: Запрашивайте только те поля, которые реально существуют в справочнике-приемнике и логически туда переносятся (Наименование, Подразделение, Должность). Выгрузка огромных вложенных таблиц сильно замедляет работу кода и потребляет лишнюю оперативную память.

Типовой подход: использование метода Заполнить()

Прежде чем писать сложные запросы, всегда проверяйте, не предусмотрел ли вендор (1С) типовой механизм ввода на основании. В 1С существует стандартный метод Заполнить(), который вызывает обработчик ОбработкаЗаполнения в модуле объекта.

Если справочник рабочих мест поддерживает ввод на основании штатного расписания, всю логику можно свести к двум строкам:


РабМесто = Справочники.РабочиеМестаОхраныТруда.СоздатьЭлемент();
РабМесто.Заполнить(СсылкаНаШтатноеРасписание);
РабМесто.Записать();

Итоговый вариант решения через запрос

Если типового заполнения нет, давайте напишем правильный, оптимизированный код для нашей задачи:


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

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

← На главную