В практике разработки на платформе 1С часто возникает задача: необходимо создать отчет, который одновременно показывает детализацию по конкретному подразделению (магазину) и при этом выводит сравнительные данные или «глобальных лидеров» по всей компании. Основная сложность заключается в том, что стандартные механизмы компоновки данных (СКД) накладывают условия отбора на весь запрос целиком, «обрезая» данные других магазинов еще на этапе получения данных из базы. В итоге, если продавец открывает отчет по своему магазину, он видит себя лучшим, так как данные о коллегах из других точек просто не попали в результат запроса.
В этой статье мы подробно разберем, как обойти это ограничение и реализовать расчет «глобального бонуса» или максимального среднего чека по всей сети, используя различные технические приемы в системе компоновки данных.
Разберем, пожалуй, самый элегантный способ, встроенный в возможности языка выражений СКД. Функция ВычислитьВыражение позволяет нам обращаться к данным, выходя за рамки текущей группировки.
Проанализируем ситуацию: если мы просто добавим ресурс с расчетом максимума, он будет учитывать только те записи, которые прошли через фильтр настроек. Чтобы получить данные по всей сети, нам нужно изменить область поиска. Рассмотрим синтаксис функции для ресурса:
ВычислитьВыражение("Максимум(СреднийЧек)", "ОбщийИтог")
Однако здесь есть важный нюанс. Чтобы эта функция «увидела» данные других магазинов, эти данные должны физически присутствовать в результирующей таблице компоновки. Если пользователь установил «Общий отбор» на весь отчет по полю Магазин, СКД добавит условие ГДЕ в основной запрос, и данные других магазинов не попадут в отчет.
Алгоритм решения:
Если нам нужно, чтобы расчет максимального чека происходил максимально эффективно на уровне SQL, мы можем подготовить данные заранее внутри текста запроса. Рассмотрим, как это сделать, избежав автоматического наложения фильтров СКД.
Выясним причину проблемы: механизм Автозаполнения в СКД ищет поля, по которым установлен отбор, и автоматически добавляет условия во все части запроса, где эти поля упоминаются. Чтобы этого избежать, мы можем использовать вложенный запрос без расширений языка запросов.
Посмотрим на пример структуры запроса:
ВЫБРАТЬ
Продажи.Магазин КАК Магазин,
Продажи.Сотрудник КАК Сотрудник,
Продажи.Сумма / Продажи.КоличествоЧеков КАК СреднийЧек,
ГлобальныеДанные.МаксимальныйЧекПоСети КАК МаксимальныйЧекПоСети
ИЗ
РегистрНакопления.Продажи КАК Продажи
ЛЕВОЕ СОЕДИНЕНИЕ (
ВЫБРАТЬ
МАКСИМУМ(Т.Сумма / Т.КоличествоЧеков) КАК МаксимальныйЧекПоСети
ИЗ
РегистрНакопления.Продажи КАК Т
) КАК ГлобальныеДанные ПО ИСТИНА
{ГДЕ
Продажи.Магазин.* КАК Магазин}
Обратите внимание: во вложенном запросе мы намеренно не используем фигурные скобки {}. Благодаря этому, когда СКД применит пользовательский отбор по магазину, она добавит его только к основной таблице Продажи, а подзапрос ГлобальныеДанные выполнится без ограничений и вернет истинный максимум по всей сети.
Проанализируем еще один мощный инструмент — использование двух независимых наборов данных типа «Запрос». Это позволяет полностью изолировать логику получения локальных данных от глобальных показателей.
Шаги реализации:
МАКСИМУМ(СреднийЧек) по всей таблице. В этом наборе данных отключаем автозаполнение или просто не указываем поле Магазин в списке доступных полей.Ключ со значением 1).Важный момент: При настройке связи в колонке «Параметры» ничего не указываем. Это заставит систему выполнить второй запрос один раз и «приклеить» его результат к каждой строке первого набора, независимо от того, какой магазин выбран в фильтре.
Иногда проще всего вычислить «Победителя по сети» отдельно и передать это значение в отчет как готовый параметр. Это особенно удобно, если данные о лидере нужно вывести просто в шапке отчета.
Рассмотрим пример кода в модуле объекта отчета:
Процедура ПриКомпоновкеРезультата(ДанныеРасшифровки, СтандартнаяОбработка)
// 1. Вычисляем максимум отдельным запросом
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ МАКСИМУМ(СреднийЧек) ИЗ РегистрПродаж";
Результат = Запрос.Выполнить().Выгрузить();
ЗначениеМаксимума = 0;
Если Результат.Количество() > 0 Тогда
ЗначениеМаксимума = Результат[0][0];
КонецЕсли;
// 2. Устанавливаем значение в параметры СКД
ПараметрМаксимум = КомпоновщикНастроек.Настройки.ПараметрыДанных.Элементы.Найти("МаксимумПоСети");
Если ПараметрМаксимум <> Неопределено Тогда
ПараметрМаксимум.Значение = ЗначениеМаксимума;
ПараметрМаксимум.Использование = Истина;
КонецЕсли;
КонецПроцедуры
В самой схеме СКД вам останется только создать параметр МаксимумПоСети и вывести его в выбранные поля или в заголовок отчета.
Выясним важную техническую деталь, о которой часто забывают. Если в базе настроен RLS (Record Level Security), то есть права доступа сотрудников ограничены только их магазином на уровне платформы, ни один из вышеперечисленных методов (кроме программного расчета под привилегированным режимом) не сработает.
Почему это происходит? Платформа автоматически добавляет условия ограничения прав к любому запросу. Если у пользователя нет прав на чтение записей других магазинов, база данных вернет NULL или 0 по всем «чужим» записям. В такой ситуации расчет «глобального бонуса» возможен только если отчет формируется на стороне сервера в модуле с установленной галочкой Привилегированный или под правами менеджера, у которого есть доступ ко всем подразделениям.
Подводя итог, выбор метода зависит от ваших архитектурных предпочтений: функция ВычислитьВыражение подходит для быстрых правок в настройках, а использование нескольких наборов данных или программная установка параметров гарантируют более стабильный результат в сложных отчетах.