Как исправить ошибку «Поле объекта не доступно для записи» при заполнении колонки в форме списка 1С?

Программист 1С v8.3 (Обычные формы)
← На главную

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

Почему возникает ошибка записи в данные строки?

Проанализируем ситуацию: когда мы работаем с формой списка (например, ДокументСписок), система считывает данные из базы данных и предоставляет их нам в виде объекта ДанныеСтроки. Этот объект имеет тип Ссылка (например, ДокументСсылка.ПоступлениеТоваровУслуг). В архитектуре 1С ссылки являются неизменяемыми объектами. Они предназначены для идентификации записи в базе, а не для редактирования ее реквизитов.

Когда мы пытаемся выполнить код вроде ДанныеСтроки.МояКолонка = Значение, система блокирует это действие, так как ДанныеСтроки — это проекция данных из базы, доступная только для чтения. Даже если вы попытаетесь получить объект через ПолучитьОбъект(), это не поможет отобразить данные в конкретной ячейке списка "на лету", так как список уже сформирован.

Решение №1: Использование события ПриВыводеСтроки (для простых задач)

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

Разберем по шагам, как это реализовать:

  1. Добавим в табличное поле новую колонку через конфигуратор (без привязки к данным).
  2. В обработчике события ПриВыводеСтроки обратимся к ячейке этой колонки.
  3. Установим нужный текст.

Пример кода для реализации:


Процедура ДокументСписокПриВыводеСтроки(Элемент, ОформлениеСтроки, ДанныеСтроки)
    // Проверяем, что данные строки доступны
    Если ДанныеСтроки = Неопределено Тогда
        Возврат;
    КонецЕсли;

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

Важный нюанс: Этот метод прост, но имеет огромный минус в производительности. Если в списке отображается 30 строк, то при каждой прокрутке система будет выполнять 30 отдельных запросов к базе данных. Это называется проблемой "N+1 запроса" и может привести к сильным "тормозам" интерфейса.

Решение №2: Профессиональный подход через ПриПолученииДанных (оптимизация)

Чтобы избежать проблем с производительностью, проанализируем событие ПриПолученииДанных. В отличие от предыдущего метода, оно вызывается один раз для всей пачки строк, которые система готовит к выводу на экран. Это позволяет нам выполнить **один пакетный запрос** ко всем видимым документам сразу.

Рассмотрим алгоритм правильной реализации:

  1. Соберем все ссылки отображаемых строк в массив.
  2. Одним запросом выберем все нужные данные для этих ссылок.
  3. Поместим результат запроса в соответствие (ключ — ссылка документа, значение — искомые данные).
  4. Обойдем коллекцию ОформленияСтрок и заполним ячейки данными из соответствия.

Пример оптимизированного кода:


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

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

    // 4. Заполняем ячейки оформления
    Для Каждого Оформление Из ОформленияСтрок Цикл
        СсылкаНаДокумент = Оформление.ДанныеСтроки.Ссылка;
        ДокументЦен = СоответствиеДанных.Получить(СсылкаНаДокумент);
        
        Если ДокументЦен <> Неопределено Тогда
            Ячейка = Оформление.Ячейки.ДокументУстановкиЦен;
            Ячейка.УстановитьТекст(Строка(ДокументЦен));
            // Если хотим, чтобы в ячейке была именно ссылка (для открытия по клику)
            Ячейка.Значение = ДокументЦен;
            Ячейка.ОтображатьТекст = Истина;
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры

Как сделать так, чтобы по ссылке можно было "провалиться"?

Выясним причину, по которой метод УстановитьТекст() иногда кажется недостаточным. Если вам нужно, чтобы пользователь мог нажать на ячейку и открыть документ, простого текста недостаточно. Для этого необходимо соблюсти два условия:

  1. Тип значения колонки: В свойствах колонки табличного поля в конфигураторе должен быть указан тип данных (например, ДокументСсылка.УстановкаЦенНоменклатуры). Если тип не указан, система воспримет значение как строку.
  2. Свойство Значение: Вместо (или вместе с) УстановитьТекст() используйте присвоение Ячейка.Значение = СсылкаИзЗапроса;. Это позволит платформе обрабатывать ячейку как полноценный объект 1С.

Посмотрим на важную деталь: если колонка в табличном поле не связана с данными (свойство Данные пустое), то присвоение Ячейка.Значение будет работать корректно и позволит открывать форму документа стандартными средствами.

Особенности работы с табличными частями в запросах

Проанализируем ситуацию, когда данные нужно вытащить не из шапки документа, а из его табличной части. В запросе автора темы использовалось условие УстановкаЦенНоменклатуры.Товары.ДокументПоступления = &ДокументПоступления. Нужно помнить, что такое обращение может привести к дублированию строк, если один и тот же документ поступления встречается в нескольких строках табличной части "Товары" одного документа установки цен.

Чтобы избежать путаницы в списке, рекомендуем использовать ключевое слово РАЗЛИЧНЫЕ в тексте запроса:


Запрос.Текст = 
"ВЫБРАТЬ РАЗЛИЧНЫЕ
|    УстановкаЦенТовары.ДокументПоступления КАК Поступление,
|    УстановкаЦенТовары.Ссылка КАК Ссылка
|ИЗ
|    Документ.УстановкаЦенНоменклатуры.Товары КАК УстановкаЦенТовары
|ГДЕ
|    УстановкаЦенТовары.ДокументПоступления В (&МассивСсылок)";

Подведем итоги

Мы выяснили, что ошибка «Поле объекта не доступно для записи» возникает из-за специфики работы с коллекцией ДанныеСтроки, которая возвращает неизменяемые ссылки. Для динамического вывода данных в форму списка следует использовать объект ОформлениеСтроки и его коллекцию Ячейки. При этом крайне важно использовать событие ПриПолученииДанных для оптимизации запросов к базе данных, чтобы обеспечить высокую скорость работы интерфейса даже при больших объемах данных.

← На главную