Часто начинающие программисты 1С сталкиваются с ситуацией: написан запрос к виртуальной таблице регистра сведений, в консоли запросов он отрабатывает идеально, а в программном коде возвращает пустой результат или вызывает непонятную ошибку. Рассмотрим типичную ситуацию получения цен на материалы и проанализируем, где могут скрываться ошибки и как их правильно исправить.
Представим, что мы написали следующий код для получения цены и валюты:
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ РАЗРЕШЕННЫЕ ПЕРВЫЕ 1
| ЦеныМатериаловАзСрезПоследних.Цена,
| ЦеныМатериаловАзСрезПоследних.Валюта
|ИЗ
| РегистрСведений.ЦеныМатериаловАз.СрезПоследних(
| &Дата,
| Организация = &Организация
| И ТипыЦен = ЗНАЧЕНИЕ(Перечисление.ТипыЦен.Производство)
| И Материал = &Материал) КАК ЦеныМатериаловАзСрезПоследних";
Запрос.УстановитьПараметр("Дата", Новый Граница(Объект.Дата, ВидГраницы.Включая));
Запрос.УстановитьПараметр("Организация", Объект.Организация);
Запрос.УстановитьПараметр("Материал", Объект.Виноград);
Выборка = Запрос.Выполнить.Выбрать;
Выборка.Следующий;
Объект.ЦенаПродукта = Выборка.Цена;
Сначала обратим внимание на синтаксические ошибки. В языке 1С вызов методов обязательно требует круглых скобок. Конструкция Запрос.Выполнить.Выбрать вызовет ошибку. Правильное написание выглядит так:
Выборка = Запрос.Выполнить().Выбрать();
Выборка.Следующий();
Выясним причину, по которой запрос не находит данные:
Объект.Виноград. Если пользователь на форме еще не выбрал материал (поле пустое), запрос ничего не найдет. Перед выполнением запроса всегда проверяйте, заполнены ли ключевые параметры.Новый Граница(Объект.МоментВремени(), ВидГраницы.Включая). Если же документ создается и еще не сохранен в базу, лучше использовать ТекущаяДатаСеанса().Пытаясь исправить проблему, разработчик часто выносит код в отдельную серверную функцию и возвращает результат на клиентскую сторону:
&НаСервере
Функция ПолучитьЦенуВинограта()
// ... выполнение запроса ...
Выборка = Запрос.Выполнить().Выбрать();
Возврат Выборка;
КонецФункции
При вызове такой функции возникает критическая ошибка: Ошибка передачи данных между клиентом и сервером. Значение недопустимого типа. Ошибка отображения типов: Отсутствует отображение для типа 'ВыборкаИзРезультатаЗапроса'.
Разберем подробнее, почему это происходит. В управляемом приложении 1С четко разделен контекст: есть Клиент (пользовательский интерфейс) и Сервер (работа с базой данных). Объекты, напрямую связанные с СУБД, такие как Запрос, РезультатЗапроса и ВыборкаИзРезультатаЗапроса, существуют исключительно на сервере. Платформа физически не умеет передавать (сериализовать) их по сети на клиент, так как для них нет соответствующего описания в формате XDTO.
Посмотрим на правильное решение. Серверная функция должна извлечь конкретные данные из выборки и вернуть их в виде примитивных типов (число, строка) или универсальных коллекций (массив, структура). Так как нам нужно вернуть и цену, и валюту, идеальным вариантом будет использование структуры:
&НаСервере
Функция ПолучитьЦенуИВалютуСервер(Материал, Организация, ДатаДокумента)
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
| ЦеныМатериалов.Цена КАК Цена,
| ЦеныМатериалов.Валюта КАК Валюта
|ИЗ
| РегистрСведений.ЦеныМатериаловАз.СрезПоследних(
| &Период,
| Организация = &Организация
| И ТипыЦен = ЗНАЧЕНИЕ(Перечисление.ТипыЦен.Производство)
| И Материал = &Материал) КАК ЦеныМатериалов";
Запрос.УстановитьПараметр("Период", Новый Граница(ДатаДокумента, ВидГраницы.Включая));
Запрос.УстановитьПараметр("Организация", Организация);
Запрос.УстановитьПараметр("Материал", Материал);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
Результат = Новый Структура("Цена, Валюта", Выборка.Цена, Выборка.Валюта);
Возврат Результат;
Иначе
Возврат Неопределено;
КонецЕсли;
КонецФункции
В первоначальном коде автор использует оператор ПЕРВЫЕ 1. При работе с виртуальной таблицей СрезПоследних это избыточно. Логика самого регистра сведений устроена так, что для уникальной комбинации измерений (Организация, ТипЦен, Материал) на конкретную дату система и так вернет только одну, наиболее актуальную запись. Конструкция ПЕРВЫЕ 1 здесь не несет пользы.
Зато автор абсолютно правильно разместил условия (отборы) внутри параметров виртуальной таблицы: Организация = &Организация И Материал = &Материал. Разберем по шагам, почему это важно: если бы эти условия были написаны в секции ГДЕ, платформа сначала получила бы срез последних цен абсолютно по всем материалам и организациям в базе, и только потом отфильтровала бы нужный виноград. Помещение отборов внутрь параметров виртуальной таблицы многократно ускоряет выполнение запроса и является правилом хорошего тона при разработке на 1С.