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