При работе с отчетами в 1С v7.7 пользователи иногда сталкиваются с ситуациями, когда фильтрация данных не работает так, как ожидается. В данной теме мы рассмотрим конкретный случай, когда фильтр по условию "Стоимость больше" не применялся корректно, и разберем, как устранить эту проблему.
Первоначально пользователь сообщил о проблеме "не работает сортировка", однако дальнейшее уточнение показало, что речь идет не о сортировке (упорядочивании) данных, а именно о фильтрации, то есть отборе строк, сумма по которым превышает указанное значение.
Давайте проанализируем предоставленный код процедуры Сформировать_(), которая отвечает за формирование отчета. Основная проблема кроется в том, как и на каком уровне применяется условие фильтрации по сумме.
Ниже представлен проблемный участок кода, демонстрирующий вложенные циклы группировки запроса и блок, содержащий условие фильтрации:
// ...
Пока Запрос.Группировка(1) = 1 Цикл // Группировка по Отделу
// ...
Пока Запрос.Группировка(2) = 1 Цикл // Группировка по Документам
// ...
Пока Запрос.Группировка(3) = 1 Цикл // Группировка по Вотчет
// ...
Пока Запрос.Группировка(4) = 1 Цикл // Группировка по Объекту
// ... инициализация переменных ...
Пока Запрос.Группировка(5) = 1 Цикл // Группировка по ТекДок (документу)
// ... здесь определяется СуммаДог для текущего документа ...
Если Запрос.Документы.Вид() = "ДоговорНаПроектирование" Тогда
// ...
СуммаДог = ТабЗДоговоров.СуммаДог; // Или Запрос.СуммаДог
// ...
КонецЕсли;
Если Запрос.Документы.Вид() = "ДопСоглашение" Тогда
// ...
СуммаДог = ТабЗДопСоглашения.ОбщСуммаДог; // Или Запрос.Документы.Сумма
// ...
КонецЕсли;
КонецЦикла; // Запрос.Группировка(5) завершилась
// В этом месте применяется условие фильтрации
Если ПустаяСтрока(Стоим) <> 1 Тогда
Если Число(СуммаДог) > Число(Стоим) Тогда // Проблемное условие
Номер = Номер + 1;
Таб.ВывестиСекцию("Данные");
_СуммаДог = _СуммаДог + СуммаДог;
КонецЕсли;
Иначе
Номер = Номер + 1;
Таб.ВывестиСекцию("Данные");
_СуммаДог = _СуммаДог + СуммаДог;
КонецЕсли;
КонецЦикла; // Запрос.Группировка(4)
// ...
КонецЦикла;
КонецЦикла;
КонецЦикла;
Проблема заключается в том, что условие Если Число(СуммаДог) > Число(Стоим) Тогда применяется не к агрегированной (общей) сумме по текущей группировке "Объект", а к переменной СуммаДог, значение которой было установлено в самом внутреннем цикле (по ТекДок). Это означает следующее:
Пока Запрос.Группировка(5) = 1 Цикл переменная СуммаДог получает значение суммы для каждого отдельного документа (договора или доп. соглашения).КонецЦикла; для Группировка(5)), переменная СуммаДог сохраняет значение суммы последнего обработанного документа в текущей группировке по "Объекту".Если Число(СуммаДог) > Число(Стоим) Тогда, он сравнивает значение Стоим не с общей суммой всех документов для данного объекта, а только с суммой этого последнего документа.Таким образом, фильтр срабатывает некорректно, поскольку он не учитывает совокупную стоимость объекта, а ориентируется лишь на один из документов.
Для того чтобы фильтр "Стоимость больше" работал правильно, нам необходимо сравнивать заданное значение (Стоим) с агрегированной суммой по каждому Объекту, а не с суммой отдельного документа. Рассмотрим два основных подхода к решению этой задачи.
Наиболее эффективным и правильным способом является использование агрегатной функции ОбщСуммаДог = Приход(СуммаДог);, которая уже определена в вашем запросе. Эта функция вычисляет общую сумму по полю СуммаДог для текущего уровня группировки. Мы можем получить эту агрегированную сумму на уровне группировки по Объекту и применить к ней условие фильтрации.
Разберем по шагам, как модифицировать код:
СуммаДог, например: |Функция ОбщСуммаДог = Приход(СуммаДог);. В вашем коде она уже есть.Запрос.ОбщСуммаДог на уровне группировки по Объекту.Посмотрим на пример модификации:
// ...
Пока Запрос.Группировка(1) = 1 Цикл // Группировка по Отделу
// ...
Пока Запрос.Группировка(2) = 1 Цикл // Группировка по Документам
// ...
Пока Запрос.Группировка(3) = 1 Цикл // Группировка по Вотчет
// ...
Пока Запрос.Группировка(4) = 1 Цикл // Группировка по Объекту
// Здесь Запрос.Объект представляет текущий объект, а Запрос.ОбщСуммаДог
// содержит сумму всех договоров и доп.соглашений для данного объекта в текущем периоде.
// В этом месте Запрос.ОбщСуммаДог уже содержит общую сумму по текущему Объекту.
// Переменные СуммаДог и ДогДатаС/По могут быть получены внутри цикла (5),
// но для фильтрации нам нужна общая сумма по Объекту.
Если ПустаяСтрока(Стоим) <> 1 Тогда
Если Число(Запрос.ОбщСуммаДог) > Число(Стоим) Тогда // Применяем фильтр к агрегированной сумме Объекта
Номер = Номер + 1;
Таб.ВывестиСекцию("Данные");
_СуммаДог = _СуммаДог + Запрос.ОбщСуммаДог; // Накапливаем общую сумму по отделу
КонецЕсли;
Иначе
// Если условие на Стоим не задано, выводим данные без фильтрации по сумме
Номер = Номер + 1;
Таб.ВывестиСекцию("Данные");
_СуммаДог = _СуммаДог + Запрос.ОбщСуммаДог; // Накапливаем общую сумму по отделу
КонецЕсли;
// Для корректного отображения деталей документов (ТекДок)
// вам может понадобиться дополнительный цикл по Запрос.Группировка(5)
// внутри этого блока, если отчет должен детализировать каждый документ,
// прошедший общую фильтрацию по объекту.
// В противном случае, если отображается только сводная строка по объекту,
// цикл (5) может быть излишним для вывода, но необходимым для получения
// других деталей документов, которые не являются агрегированными (например, Прим).
// Текущий код из примера сохраняет СуммаДог и другие параметры из последнего документа.
// Возможно, вам потребуется изменить логику сбора этих данных или их отображение,
// если теперь данные выводятся на уровне Объекта.
// Например, Прим можно сделать агрегатной функцией в запросе (первые 50 символов всех примечаний),
// или получить его из первого/последнего документа группы, если это допустимо.
КонецЦикла; // Запрос.Группировка(4)
// ...
КонецЦикла;
КонецЦикла;
КонецЦикла;
В этом случае, переменную СуммаДог, определенную внутри цикла по ТекДок, больше не потребуется использовать для фильтрации на этом уровне. Важно также убедиться, что _СуммаДог (итоговая сумма по отделу) накапливается корректно, используя Запрос.ОбщСуммаДог, а не СуммаДог из внутреннего цикла, как показано в примере выше.
Если по какой-то причине (например, из-за специфики дальнейшей обработки данных внутри цикла ТекДок) невозможно использовать агрегатную функцию запроса напрямую, можно накопить сумму по Объекту в отдельной переменной внутри цикла Пока Запрос.Группировка(4) = 1 Цикл.
Посмотрим на пример такой модификации:
// ...
Пока Запрос.Группировка(4) = 1 Цикл // Группировка по Объекту
ТекущаяСуммаПоОбъекту = 0; // Инициализируем переменную для накопления суммы по текущему объекту
// ...
Пока Запрос.Группировка(5) = 1 Цикл // Группировка по ТекДок (документу)
// ... определяем СуммаДог для текущего документа ...
Если Запрос.Документы.Вид() = "ДоговорНаПроектирование" Тогда
Нс=0;
Рез=ТабЗДоговоров.НайтиЗначение(Запрос._Объект,нс,"_Объект");
Если Рез=1 Тогда
ТабЗДоговоров.ПолучитьСтрокуПоНомеру(нс);
СуммаДог = ТабЗДоговоров.СуммаДог;
// ... другие параметры ...
Иначе
СуммаДог = Запрос.СуммаДог; // Если не найдено в ТабЗДоговоров, берем из запроса
// ... другие параметры ...
КонецЕсли;
КонецЕсли;
Если Запрос.Документы.Вид() = "ДопСоглашение" Тогда
Нс=0;
Рез=ТабЗДопСоглашения.НайтиЗначение(Запрос._Объект,нс,"_Объект");
Если Рез=1 Тогда
ТабЗДопСоглашения.ПолучитьСтрокуПоНомеру(нс);
СуммаДог = ТабЗДопСоглашения.ОбщСуммаДог; // Здесь, возможно, нужна СуммаДопСогл, а не ОбщСуммаДог
// ... другие параметры ...
Иначе
// Если не найдено в ТабЗДопСоглашения, берем из запроса
// Здесь, вероятно, нужна Запрос.СуммаДопСогл, если это для ДопСоглашения
// Или же Запрос.СуммаДог, если в запросе ДопСоглашение.СуммаДог
СуммаДог = Запрос.СуммаДог; // Или Запрос.СуммаДопСогл
// ... другие параметры ...
КонецЕсли;
КонецЕсли;
// Накапливаем сумму для текущего Объекта
ТекущаяСуммаПоОбъекту = ТекущаяСуммаПоОбъекту + СуммаДог;
КонецЦикла; // Запрос.Группировка(5)
// Здесь ТекущаяСуммаПоОбъекту содержит сумму всех документов для данного объекта
Если ПустаяСтрока(Стоим) <> 1 Тогда
Если Число(ТекущаяСуммаПоОбъекту) > Число(Стоим) Тогда
Номер = Номер + 1;
Таб.ВывестиСекцию("Данные");
_СуммаДог = _СуммаДог + ТекущаяСуммаПоОбъекту; // Накапливаем общую сумму по отделу
КонецЕсли;
Иначе
Номер = Номер + 1;
Таб.ВывестиСекцию("Данные");
_СуммаДог = _СуммаДог + ТекущаяСуммаПоОбъекту; // Накапливаем общую сумму по отделу
КонецЕсли;
КонецЦикла; // Запрос.Группировка(4)
Этот подход является менее оптимальным, так как требует дополнительных вычислений в коде, тогда как агрегатная функция ОбщСуммаДог уже доступна из запроса. Всегда отдавайте предпочтение максимально возможному использованию средств запроса для агрегации и фильтрации.
При работе с отчетами и запросами в 1С v7.7 важно учитывать не только корректность логики, но и производительность. Рассмотрим несколько общих рекомендаций:
Упорядочить По (ORDER BY) непосредственно в тексте запроса. Например, добавьте |Упорядочить По _Объект, ДатаНачПроект; после всех группировок. Это позволит базе данных отдать уже упорядоченные данные, снижая нагрузку на клиентскую часть. Для сортировки по убыванию используйте знак минус (например, |Упорядочить По ДатаОконПроект-;).ТаблицаЗначений, можно использовать метод ТЗ.Сортировать(). Этот метод выполняет сортировку уже загруженных в оперативную память данных. Пример: Тз.Сортировать("_Объект,ДатаНачПроект+").Условие. Это позволяет отсекать ненужные данные до их выгрузки в оперативную память. Пример: |Условие (Стадия.Код <> Строка(1));.Запрос.Выполнить(). В процедурах ЗаполнитьТЗДог и ЗаполнитьТЗДоп запрос выполняется дважды, что является избыточным. Достаточно выполнить его один раз и затем проверить результат.ИТОГИ ПО с последующим созданием нового запроса или объединением результатов нескольких запросов).ТекДок.Объект.Архив) внутри циклов может быть медленным. Если значение свойства не меняется внутри цикла, его лучше получить один раз до начала цикла и использовать кэшированное значение.Дата() или переменные типа "Дата" вместо строковых представлений дат (например, Дата1='01.01.10').Применяя эти принципы, вы сможете создавать более производительные и корректно работающие отчеты в 1С v7.7.