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