При разработке интерфейсов на обычных формах в 1С:Предприятие 8 программисты часто сталкиваются с необходимостью вывести в список документов или справочников дополнительную информацию, которой нет в основной таблице базы данных. Например, в списке поступлений товаров нужно отобразить связанный документ установки цен. Попытка записать данные напрямую в строку списка приводит к ошибке «Поле объекта не доступно для записи». В этой статье мы подробно разберем, почему это происходит и как правильно реализовать вывод данных, используя механизмы оформления строк.
Проанализируем ситуацию: когда мы работаем с формой списка (например, ДокументСписок), система считывает данные из базы данных и предоставляет их нам в виде объекта ДанныеСтроки. Этот объект имеет тип Ссылка (например, ДокументСсылка.ПоступлениеТоваровУслуг). В архитектуре 1С ссылки являются неизменяемыми объектами. Они предназначены для идентификации записи в базе, а не для редактирования ее реквизитов.
Когда мы пытаемся выполнить код вроде ДанныеСтроки.МояКолонка = Значение, система блокирует это действие, так как ДанныеСтроки — это проекция данных из базы, доступная только для чтения. Даже если вы попытаетесь получить объект через ПолучитьОбъект(), это не поможет отобразить данные в конкретной ячейке списка "на лету", так как список уже сформирован.
Рассмотрим самый простой способ визуального отображения данных. У объекта ТабличноеПоле есть событие ПриВыводеСтроки. Оно вызывается каждый раз, когда строка отрисовывается на экране. Здесь мы можем получить доступ к коллекции ячеек оформления и изменить их текст или значение.
Разберем по шагам, как это реализовать:
ПриВыводеСтроки обратимся к ячейке этой колонки.Пример кода для реализации:
Процедура ДокументСписокПриВыводеСтроки(Элемент, ОформлениеСтроки, ДанныеСтроки)
// Проверяем, что данные строки доступны
Если ДанныеСтроки = Неопределено Тогда
Возврат;
КонецЕсли;
// Выполняем запрос для поиска связанных данных
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| УстановкаЦен.Ссылка
|ИЗ
| Документ.УстановкаЦенНоменклатуры КАК УстановкаЦен
|ГДЕ
| УстановкаЦен.ДокументОснование = &ДокументОснование";
Запрос.УстановитьПараметр("ДокументОснование", ДанныеСтроки.Ссылка);
РезультатЗапроса = Запрос.Выполнить();
Если Не РезультатЗапроса.Пустой() Тогда
Выборка = РезультатЗапроса.Выбрать();
Выборка.Следующий();
// Устанавливаем текст в нужную ячейку
ОформлениеСтроки.Ячейки.ДокументУстановкиЦен.УстановитьТекст(Строка(Выборка.Ссылка));
КонецЕсли;
КонецПроцедуры
Важный нюанс: Этот метод прост, но имеет огромный минус в производительности. Если в списке отображается 30 строк, то при каждой прокрутке система будет выполнять 30 отдельных запросов к базе данных. Это называется проблемой "N+1 запроса" и может привести к сильным "тормозам" интерфейса.
Чтобы избежать проблем с производительностью, проанализируем событие ПриПолученииДанных. В отличие от предыдущего метода, оно вызывается один раз для всей пачки строк, которые система готовит к выводу на экран. Это позволяет нам выполнить **один пакетный запрос** ко всем видимым документам сразу.
Рассмотрим алгоритм правильной реализации:
ОформленияСтрок и заполним ячейки данными из соответствия.Пример оптимизированного кода:
Процедура ДокументСписокПриПолученииДанных(Элемент, ОформленияСтрок)
// 1. Собираем ссылки строк, которые нужно обработать
МассивСсылок = Новый Массив;
Для Каждого Оформление Из ОформленияСтрок Цикл
МассивСсылок.Добавить(Оформление.ДанныеСтроки.Ссылка);
КонецЦикла;
// 2. Получаем данные одним запросом
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| УстановкаЦен.ДокументОснование КАК ДокументОснование,
| УстановкаЦен.Ссылка КАК СсылкаУстановки
|ИЗ
| Документ.УстановкаЦенНоменклатуры КАК УстановкаЦен
|ГДЕ
| УстановкаЦен.ДокументОснование В (&МассивСсылок)";
Запрос.УстановитьПараметр("МассивСсылок", МассивСсылок);
ТаблицаДанных = Запрос.Выполнить().Выгрузить();
// 3. Создаем соответствие для быстрого поиска
СоответствиеДанных = Новый Соответствие;
Для Каждого СтрокаТЗ Из ТаблицаДанных Цикл
СоответствиеДанных.Вставить(СтрокаТЗ.ДокументОснование, СтрокаТЗ.СсылкаУстановки);
КонецЦикла;
// 4. Заполняем ячейки оформления
Для Каждого Оформление Из ОформленияСтрок Цикл
СсылкаНаДокумент = Оформление.ДанныеСтроки.Ссылка;
ДокументЦен = СоответствиеДанных.Получить(СсылкаНаДокумент);
Если ДокументЦен <> Неопределено Тогда
Ячейка = Оформление.Ячейки.ДокументУстановкиЦен;
Ячейка.УстановитьТекст(Строка(ДокументЦен));
// Если хотим, чтобы в ячейке была именно ссылка (для открытия по клику)
Ячейка.Значение = ДокументЦен;
Ячейка.ОтображатьТекст = Истина;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Выясним причину, по которой метод УстановитьТекст() иногда кажется недостаточным. Если вам нужно, чтобы пользователь мог нажать на ячейку и открыть документ, простого текста недостаточно. Для этого необходимо соблюсти два условия:
ДокументСсылка.УстановкаЦенНоменклатуры). Если тип не указан, система воспримет значение как строку.УстановитьТекст() используйте присвоение Ячейка.Значение = СсылкаИзЗапроса;. Это позволит платформе обрабатывать ячейку как полноценный объект 1С.Посмотрим на важную деталь: если колонка в табличном поле не связана с данными (свойство Данные пустое), то присвоение Ячейка.Значение будет работать корректно и позволит открывать форму документа стандартными средствами.
Проанализируем ситуацию, когда данные нужно вытащить не из шапки документа, а из его табличной части. В запросе автора темы использовалось условие УстановкаЦенНоменклатуры.Товары.ДокументПоступления = &ДокументПоступления. Нужно помнить, что такое обращение может привести к дублированию строк, если один и тот же документ поступления встречается в нескольких строках табличной части "Товары" одного документа установки цен.
Чтобы избежать путаницы в списке, рекомендуем использовать ключевое слово РАЗЛИЧНЫЕ в тексте запроса:
Запрос.Текст =
"ВЫБРАТЬ РАЗЛИЧНЫЕ
| УстановкаЦенТовары.ДокументПоступления КАК Поступление,
| УстановкаЦенТовары.Ссылка КАК Ссылка
|ИЗ
| Документ.УстановкаЦенНоменклатуры.Товары КАК УстановкаЦенТовары
|ГДЕ
| УстановкаЦенТовары.ДокументПоступления В (&МассивСсылок)";
Мы выяснили, что ошибка «Поле объекта не доступно для записи» возникает из-за специфики работы с коллекцией ДанныеСтроки, которая возвращает неизменяемые ссылки. Для динамического вывода данных в форму списка следует использовать объект ОформлениеСтроки и его коллекцию Ячейки. При этом крайне важно использовать событие ПриПолученииДанных для оптимизации запросов к базе данных, чтобы обеспечить высокую скорость работы интерфейса даже при больших объемах данных.