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