Почему возникает ошибка «Объект недоступен для изменения» при вызове метода Добавить() и как её исправить в 1С

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

При разработке функционала в 1С программисты часто сталкиваются с ситуацией, когда при попытке добавить новую строку в табличную часть выводится ошибка вида: «Ошибка при вызове метода контекста (Добавить): Объект недоступен для изменения». Разберем, почему возникает эта проблема, чем отличается работа со ссылками от работы с объектами, и как правильно реализовать слияние табличных частей на сервере.

Анализ причины ошибки

Проанализируем механику работы платформ 1С с данными справочников. Когда мы находим элемент справочника с помощью функций поиска (например, Справочники.ГруппыДоступа.НайтиПоНаименованию()), платформа возвращает значение типа СправочникСсылка.

Табличная часть, полученная из ссылки (например, ВыбраннаяГруппа.Пользователи), имеет тип СправочникТабличнаяЧасть и предназначена только для чтения. Попытка вызвать методы модификации коллекции (Добавить(), Удалить(), Очистить()) у ссылочного типа всегда приводит к ошибке выполнения.

В исходном коде разработчик пытался объединить списки пользователей двух групп следующим образом:


// Ошибка: ВыбраннаяГруппа2 является ссылкой, а не объектом или таблицей значений
СортированныйСписок = ВыбраннаяГруппа1.Пользователи.Выгрузить();
Если НЕ ВыбраннаяГруппа2.Пустая() Тогда
    СортированныйСписок = ВыбраннаяГруппа2.Пользователи.Добавить(); // Вызовет исключение!
КонецЕсли;

Даже если бы ВыбраннаяГруппа2 была объектом, метод Добавить() без параметров лишь создает новую пустую строку в табличной части объекта базы данных, но не копирует туда данные из другой группы. Рассмотрев причины поведения, перейдем к правильным способам решения этой задачи.

Способ 1. Корректное объединение табличных частей в памяти

Если задача заключается только в получении и объединении данных для вывода пользователю (без изменения самих групп в базе данных), считывать объекты через ПолучитьОбъект() не нужно. Это создает лишнюю нагрузку и блокировки.

Правильный подход — выгрузить табличные части ссылок в память с помощью метода Выгрузить() (получив типы ТаблицаЗначений) и затем объединить их строки. Посмотрим на пример реализации:


&НаСервереБезКонтекста
Функция ПолучитьСписокГруппыНаСервере(НазваниеГруппы1, НазваниеГруппы2)
    
    ВыбраннаяГруппа1 = Справочники.ГруппыДоступа.НайтиПоНаименованию(НазваниеГруппы1);
    ВыбраннаяГруппа2 = Справочники.ГруппыДоступа.НайтиПоНаименованию(НазваниеГруппы2);
    
    Если ВыбраннаяГруппа1.Пустая() И ВыбраннаяГруппа2.Пустая() Тогда
        Возврат Неопределено;
    КонецЕсли;
    
    // Создаем результирующую таблицу значений
    ТаблицаРезультат = Новый ТаблицаЗначений;
    ТаблицаРезультат.Колонки.Добавить("Пользователь");
    
    // Заполняем данными первой группы
    Если НЕ ВыбраннаяГруппа1.Пустая() Тогда
        Для Каждого Стр Из ВыбраннаяГруппа1.Пользователи.Выгрузить() Цикл
            ЗаполнитьЗначенияСвойств(ТаблицаРезультат.Добавить(), Стр);
        КонецЦикла;
    КонецЕсли;
    
    // Добавляем данные второй группы
    Если НЕ ВыбраннаяГруппа2.Пустая() Тогда
        Для Каждого Стр Из ВыбраннаяГруппа2.Пользователи.Выгрузить() Цикл
            ЗаполнитьЗначенияСвойств(ТаблицаРезультат.Добавить(), Стр);
        КонецЦикла;
    КонецЕсли;
    
    // Сортируем и формируем массив
    ТаблицаРезультат.Сортировать("Пользователь Возр");
    Возврат ТаблицаРезультат.ВыгрузитьКолонку("Пользователь");
    
КонецФункции

Способ 2. Оптимальный вариант с использованием запроса (Рекомендуется)

Наиболее эффективным способом с точки зрения производительности является применение языка запросов. Запрос выполняют выборку на стороне СУБД, исключает дублирование пользователей (если один человек входит в обе группы) и сразу сортирует результат.

Разберем пример серверной функции с запросом:


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

Заполнение выпадающего списка на клиенте

Полученный на сервере массив ссылок передается на клиент. Чтобы быстро заполнить элемента формы типа СписокЗначений (например, свойство СписокВыбора у поля ввода), рекомендуется использовать метод ЗагрузитьЗначения() вместо поэлементного обхода в цикле Добавить().

Рассмотрим клиентский обработчик события:


&НаКлиенте
Процедура ПользовательНачалоВыбора(Элемент, ДанныеВыбора, СтандартнаяОбработка)
    
    // Очищаем текущий список выпадающего выбора
    Элемент.СписокВыбора.Очистить();
    
    ЧленыГруппы = ПолучитьСписокГруппыЗапросом("Проекты Полный Доступ", "Проекты Только Просмотр");
    
    Если ЧленыГруппы <> Неопределено И ЧленыГруппы.Количество() > 0 Тогда
        // Загружаем весь массив значений одним вызовом
        Элемент.СписокВыбора.ЗагрузитьЗначения(ЧленыГруппы);
    КонецЕсли;
    
КонецПроцедуры

Если действительно необходимо изменить объект в базе

Если цель разработчика — добавить новую строку в табличную часть элемента справочника с сохранением в базу данных, предварительно следует получить объект из ссылки с помощью метода ПолучитьОбъект():


// Пример записи данных в базу
ГруппаОбъект = ВыбраннаяГруппа2.ПолучитьОбъект();
НоваяСтрока = ГруппаОбъект.Пользователи.Добавить();
НоваяСтрока.Пользователь = НужныйПользователь;
ГруппаОбъект.Записать();

Подводя итог: всегда разделяйте операции чтения данных по ссылкам и операции модификации объектов. Для чтения коллекций используйте выгрузку или запросы, а для изменения элементов справочника — получайте объектную форму данных.

← На главную