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