Почему возникает ошибка «Поле объекта недоступно для записи» в процедуре ПриВыводеСтроки и как ее исправить?

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

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

Анализ исходной ситуации и причины ошибки

Посмотрим на фрагмент кода, вызвавший исключение:


Процедура СписокПриВыводеСтроки(Элемент, ОформлениеСтроки, ДанныеСтроки)
    мПоискОбъектовВСпискахДанных.СписокПриВыводеСтрокиСпискаОтбораДанных(Элемент, ОформлениеСтроки, ДанныеСтроки);
    
    ШК = ПолучитьШК(ДанныеСтроки.Ссылка);
    ДанныеСтроки.ПоискШК = ШК;
    // ОформлениеСтроки.Ячейки.Штрихкод.Значение = ШК;
КонецПроцедуры

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

Решение 1. Использование объекта ОформлениеСтроки для вывода данных

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

  1. Откроем форму списка номенклатуры в конфигураторе.
  2. В свойствах табличного поля Список добавим новую колонку, зададим ей имя (например, Штрихкод).
  3. Свойство Данные у созданной колонки обязательно оставляем пустым, так как значение будет формироваться программно при отрисовке.
  4. В коде обработчика обращаемся к нужной ячейке через свойство ОформлениеСтроки.Ячейки.

Посмотрим на пример правильного заполнения ячейки:


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

Решение 2. Оптимизация производительности через событие ПриПолученииДанных

Рассмотрим важную проблему производительности. Вызов функции ПолучитьШК(ДанныеСтроки.Ссылка) внутри ПриВыводеСтроки выполняется для каждой выводимой на экран строки отдельно (так называемая проблема N+1 запросов). При быстрой прокрутке списка или динамическом обновлении интерфейс начнет заметно зависать, так как база данных будет перегружена множеством единичных запросов.

В платформе 1С для оптимизации таких сценариев предусмотрен обработчик ПриПолученииДанных. Разберем по шагам, как перенести логику в пакетную обработку:

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

Посмотрим на программную реализацию:


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

Ограничения механизма оформления и организация поиска

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

Если пользователю требуется именно поиск по штрихкоду, используют один из следующих подходов:

  1. Поле ввода для быстрого поиска на форме: добавляется отдельное текстовое поле «Штрихкод». При вводе значения или нажатии клавиши Enter выполняется точечный поиск номенклатуры в регистре сведений, после чего курсор позиционируется на найденной строке: ЭлементыФормы.Список.ТекущаяСтрока = НайденнаяСсылка;
  2. Обработка событий торгового оборудования: используется предопределенная процедура формы ВнешнееСобытие, перехватывающая сканирование со сканера ШК и выполняющая мгновенный переход к строке.
  3. Программная фильтрация: если нужно отфильтровать список по штрихкоду, запрос находит соответствующие ссылки, а затем накладывается платформенный отбор:
    
    Список.Отбор.Ссылка.ВидСравнения = ВидСравнения.ВСписке;
    Список.Отбор.Ссылка.Значение = СписокНайденныхСсылок;
    Список.Отбор.Ссылка.Использование = Истина;
    
← На главную