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