Почему в запросе 1С 7.7 не работает отбор по измерению неопределенного типа?

Программист 1С v7.7 IT и автоматизация бизнеса
← На главную

При разработке отчетов и обработок в платформе 1С:Предприятие 7.7 разработчики нередко сталкиваются с ситуацией, когда штатный запрос возвращает пустой результат при попытке наложить условие на измерение регистра, имеющее составной (неопределенный) тип данных. При этом точно такой же запрос, запущенный непосредственно из контекста формы документа с условием по ТекущийДокумент(), отрабатывает без ошибок. Разберем по шагам, почему возникает такое поведение платформы и как правильно организовать фильтрацию по ссылке в запросе.

В чем заключается проблема

Рассмотрим типовую ситуацию. В конфигурации присутствует регистр остатков или оборотов (например, Расчеты), у которого одно из измерений (например, Док) объявлено с типом Неопределенный, так как в нем могут храниться ссылки как на различные виды документов, так и на элементы справочников.

Если в модуле формы документа выполняется следующий запрос, платформа успешно возвращает выборку:


ТекстЗапроса = "
|Период с ДатаН по ДатаК;
|ДокРейс    = Регистр.Расчеты.Док;
|Контрагент = Регистр.Расчеты.Контрагент;
|Статья     = Регистр.Расчеты.Статья;
|Сумма      = Регистр.Расчеты.Сумма;
|Группировка Контрагент;
|Группировка Статья;
|Функция ПриходЭкв = Приход(Сумма);
|Функция РасходЭкв = Расход(Сумма);
|Условие (ДокРейс = ТекущийДокумент());";

Однако если мы переносим этот запрос во внешний отчет, добавляем на диалог реквизит ПоДокументу конкретного типа (например, Документ.Рейс) и пишем в условии |Условие (ДокРейс = ПоДокументу);, запрос перестает возвращать строки. Выясним причину этой коллизии.

Анализ внутреннего формата данных 1С 7.7

Проанализируем ситуацию на уровне внутреннего строкового представления ссылок с помощью функции ЗначениеВСтрокуВнутр(). Для ссылочных типов платформа 1С 7.7 использует два принципиально разных формата хранения:

  1. Строго типизированный объект (документ конкретного вида). Внутренний код вида метаданных жестко зашит в структуру типа: {"O","0","0","79","0","0"," 15056 "} Здесь число 79 — это идентификатор конкретного вида документа в метаданных конфигурации, а 15056 — идентификатор самого документа в базе.
  2. Объект общего (неопределенного) вида. В этом случае тип документа равен нулю, а реальный вид переносится в строку идентификатора перед ID объекта: {"O","0","0","0","0","0"," 79 15056 "} Здесь в поле типа указан 0, а составная строка содержит первые 10 символов под вид документа (79) и следующие символы под идентификатор документа.

Измерение регистра с типом Неопределенный физически хранит ссылки именно во втором (обобщенном) формате. Встроенный язык запросов 1С 7.7 при проверке секции Условие выполняет строгое побайтовое сравнение внутренностей объектов без неявного приведения типов. Поскольку реквизит формы документа имеет конкретный вид, а поле регистра — обобщенный вид, условие равенства возвращает Ложь.

Решение 1. Вызов метода ТекущийДокумент() в теле запроса

Когда мы вызываем метод ТекущийДокумент() у типизированного объекта, платформа возвращает ссылку общего вида типа Документ (без привязки к конкретному виду в метаданных типа). Соответственно, самый простой способ заставить условие работать — явно вызвать данный метод непосредственно в тексте запроса:


ТекстЗапроса = "
|Период с ДатаН по ДатаК;
|Докум      = Регистр.Расчеты.ТекущийДокумент;
|ДокРейс    = Регистр.Расчеты.Док;
|Контрагент = Регистр.Расчеты.Контрагент;
|Статья     = Регистр.Расчеты.Статья;
|Сумма      = Регистр.Расчеты.Сумма;
|Группировка Контрагент;
|Группировка ДокРейс;
|Группировка Статья;
|Функция ПриходЭкв = Приход(Сумма);
|Функция РасходЭкв = Расход(Сумма);
|Условие (Контрагент = Водитель);
|Условие (ДокРейс = ПоДокументу.ТекущийДокумент());";

В этом случае платформа на каждой итерации проверки условия преобразует ссылку в формат обобщенного документа, что позволяет условию сравнения отработать корректно.

Решение 2. Подготовка ссылки общего вида перед запросом

Чтобы избежать повторных вызовов метода в теле запроса и сделать код более производительным, сформируем ссылку общего вида заранее в программном модуле с помощью нетипизированного объекта Документ:


ДокОбщий = СоздатьОбъект("Документ");
Если ДокОбщий.НайтиДокумент(ПоДокументу) = 1 Тогда
    ФильтрДокумент = ДокОбщий.ТекущийДокумент();
Иначе
    ФильтрДокумент = ПолучитьПустоеЗначение("Документ");
КонецЕсли;

ТекстЗапроса = "
|Период с ДатаН по ДатаК;
|ДокРейс    = Регистр.Расчеты.Док;
|Контрагент = Регистр.Расчеты.Контрагент;
|Статья     = Регистр.Расчеты.Статья;
|Сумма      = Регистр.Расчеты.Сумма;
|Группировка Контрагент;
|Группировка ДокРейс;
|Функция ПриходЭкв = Приход(Сумма);
|Условие (ДокРейс = ФильтрДокумент);";

В переменную ФильтрДокумент попадает ссылка в обобщенном формате, благодаря чему условие запроса отрабатывает штатно и без вызова методов внутри виртуальной таблицы.

Решение 3. Использование списка значений

Если необходимо передавать один или несколько документов в качестве фильтра, отлично зарекомендовала себя конструкция с объектом СписокЗначений и оператором В:


СписокОтбора = СоздатьОбъект("СписокЗначений");
СписокОтбора.ДобавитьЗначение(ПоДокументу.ТекущийДокумент());

ТекстЗапроса = "
|Период с ДатаН по ДатаК;
|ДокРейс = Регистр.Расчеты.Док;
|Сумма   = Регистр.Расчеты.Сумма;
|Группировка ДокРейс;
|Функция ПриходЭкв = Приход(Сумма);
|Условие (ДокРейс В СписокОтбора);";

Данный вариант удобен тем, что позволяет легко масштабировать фильтр с одного документа на произвольный перечень позиций.

Решение 4. Обход движений через объект «Регистр»

Если задача заключается только в получении движений или остатков по одному конкретному документу, использование объекта Запрос в 1С 7.7 для неопределенных измерений часто оказывается медленным. Рекомендуется использовать прямое чтение регистра:


Рег = СоздатьОбъект("Регистр.Расчеты");
// Устанавливаем фильтр по измерению (если в конфигураторе включен отбор)
Рег.УстановитьЗначениеФильтра("Док", ПоДокументу, 1);
Рег.ВыбратьДвижения(ДатаН, ДатаК);
Пока Рег.ПолучитьДвижение() = 1 Цикл
    // Обработка движений: Рег.Сумма, Рег.Контрагент и т.д.
КонецЦикла;

Метод УстановитьЗначениеФильтра() на уровне платформы выполняет необходимое сопоставление типов автоматически, что исключает ошибки при несовпадении форматов типизированной и обобщенной ссылок.

← На главную