При разработке отчетов и обработок в платформе 1С:Предприятие 7.7 разработчики нередко сталкиваются с ситуацией, когда штатный запрос возвращает пустой результат при попытке наложить условие на измерение регистра, имеющее составной (неопределенный) тип данных. При этом точно такой же запрос, запущенный непосредственно из контекста формы документа с условием по ТекущийДокумент(), отрабатывает без ошибок. Разберем по шагам, почему возникает такое поведение платформы и как правильно организовать фильтрацию по ссылке в запросе.
Рассмотрим типовую ситуацию. В конфигурации присутствует регистр остатков или оборотов (например, Расчеты), у которого одно из измерений (например, Док) объявлено с типом Неопределенный, так как в нем могут храниться ссылки как на различные виды документов, так и на элементы справочников.
Если в модуле формы документа выполняется следующий запрос, платформа успешно возвращает выборку:
ТекстЗапроса = "
|Период с ДатаН по ДатаК;
|ДокРейс = Регистр.Расчеты.Док;
|Контрагент = Регистр.Расчеты.Контрагент;
|Статья = Регистр.Расчеты.Статья;
|Сумма = Регистр.Расчеты.Сумма;
|Группировка Контрагент;
|Группировка Статья;
|Функция ПриходЭкв = Приход(Сумма);
|Функция РасходЭкв = Расход(Сумма);
|Условие (ДокРейс = ТекущийДокумент());";
Однако если мы переносим этот запрос во внешний отчет, добавляем на диалог реквизит ПоДокументу конкретного типа (например, Документ.Рейс) и пишем в условии |Условие (ДокРейс = ПоДокументу);, запрос перестает возвращать строки. Выясним причину этой коллизии.
Проанализируем ситуацию на уровне внутреннего строкового представления ссылок с помощью функции ЗначениеВСтрокуВнутр(). Для ссылочных типов платформа 1С 7.7 использует два принципиально разных формата хранения:
{"O","0","0","79","0","0"," 15056 "}
Здесь число 79 — это идентификатор конкретного вида документа в метаданных конфигурации, а 15056 — идентификатор самого документа в базе.
{"O","0","0","0","0","0"," 79 15056 "}
Здесь в поле типа указан 0, а составная строка содержит первые 10 символов под вид документа (79) и следующие символы под идентификатор документа.Измерение регистра с типом Неопределенный физически хранит ссылки именно во втором (обобщенном) формате. Встроенный язык запросов 1С 7.7 при проверке секции Условие выполняет строгое побайтовое сравнение внутренностей объектов без неявного приведения типов. Поскольку реквизит формы документа имеет конкретный вид, а поле регистра — обобщенный вид, условие равенства возвращает Ложь.
Когда мы вызываем метод ТекущийДокумент() у типизированного объекта, платформа возвращает ссылку общего вида типа Документ (без привязки к конкретному виду в метаданных типа). Соответственно, самый простой способ заставить условие работать — явно вызвать данный метод непосредственно в тексте запроса:
ТекстЗапроса = "
|Период с ДатаН по ДатаК;
|Докум = Регистр.Расчеты.ТекущийДокумент;
|ДокРейс = Регистр.Расчеты.Док;
|Контрагент = Регистр.Расчеты.Контрагент;
|Статья = Регистр.Расчеты.Статья;
|Сумма = Регистр.Расчеты.Сумма;
|Группировка Контрагент;
|Группировка ДокРейс;
|Группировка Статья;
|Функция ПриходЭкв = Приход(Сумма);
|Функция РасходЭкв = Расход(Сумма);
|Условие (Контрагент = Водитель);
|Условие (ДокРейс = ПоДокументу.ТекущийДокумент());";
В этом случае платформа на каждой итерации проверки условия преобразует ссылку в формат обобщенного документа, что позволяет условию сравнения отработать корректно.
Чтобы избежать повторных вызовов метода в теле запроса и сделать код более производительным, сформируем ссылку общего вида заранее в программном модуле с помощью нетипизированного объекта Документ:
ДокОбщий = СоздатьОбъект("Документ");
Если ДокОбщий.НайтиДокумент(ПоДокументу) = 1 Тогда
ФильтрДокумент = ДокОбщий.ТекущийДокумент();
Иначе
ФильтрДокумент = ПолучитьПустоеЗначение("Документ");
КонецЕсли;
ТекстЗапроса = "
|Период с ДатаН по ДатаК;
|ДокРейс = Регистр.Расчеты.Док;
|Контрагент = Регистр.Расчеты.Контрагент;
|Статья = Регистр.Расчеты.Статья;
|Сумма = Регистр.Расчеты.Сумма;
|Группировка Контрагент;
|Группировка ДокРейс;
|Функция ПриходЭкв = Приход(Сумма);
|Условие (ДокРейс = ФильтрДокумент);";
В переменную ФильтрДокумент попадает ссылка в обобщенном формате, благодаря чему условие запроса отрабатывает штатно и без вызова методов внутри виртуальной таблицы.
Если необходимо передавать один или несколько документов в качестве фильтра, отлично зарекомендовала себя конструкция с объектом СписокЗначений и оператором В:
СписокОтбора = СоздатьОбъект("СписокЗначений");
СписокОтбора.ДобавитьЗначение(ПоДокументу.ТекущийДокумент());
ТекстЗапроса = "
|Период с ДатаН по ДатаК;
|ДокРейс = Регистр.Расчеты.Док;
|Сумма = Регистр.Расчеты.Сумма;
|Группировка ДокРейс;
|Функция ПриходЭкв = Приход(Сумма);
|Условие (ДокРейс В СписокОтбора);";
Данный вариант удобен тем, что позволяет легко масштабировать фильтр с одного документа на произвольный перечень позиций.
Если задача заключается только в получении движений или остатков по одному конкретному документу, использование объекта Запрос в 1С 7.7 для неопределенных измерений часто оказывается медленным. Рекомендуется использовать прямое чтение регистра:
Рег = СоздатьОбъект("Регистр.Расчеты");
// Устанавливаем фильтр по измерению (если в конфигураторе включен отбор)
Рег.УстановитьЗначениеФильтра("Док", ПоДокументу, 1);
Рег.ВыбратьДвижения(ДатаН, ДатаК);
Пока Рег.ПолучитьДвижение() = 1 Цикл
// Обработка движений: Рег.Сумма, Рег.Контрагент и т.д.
КонецЦикла;
Метод УстановитьЗначениеФильтра() на уровне платформы выполняет необходимое сопоставление типов автоматически, что исключает ошибки при несовпадении форматов типизированной и обобщенной ссылок.