При работе со старыми конфигурациями 1С (такими как «Управление торговлей 10.3», «УПП 1.3» или «Комплексная автоматизация 1.1») часто возникает задача доработки «Универсального отчета». Одна из самых распространенных проблем — необходимость вывести в одном отчете не один фиксированный тип цен, а сразу несколько (например, «Закупочную», «Оптовую» и «Розничную»), причем каждая должна располагаться в своей отдельной колонке.
В этой статье мы подробно разберем, как модифицировать исходный запрос и программную логику отчета, чтобы добиться желаемого результата, избежав при этом типичных ошибок вроде дублирования остатков.
Рассмотрим стандартный подход, который часто используется в запросах. Обычно в виртуальной таблице СрезПоследних регистра сведений ЦеныНоменклатуры устанавливается жесткий фильтр по одному типу цен через параметр: ТипЦен = &БазовыйТипЦен. Это приводит к тому, что запрос возвращает только одну цену для каждой позиции номенклатуры.
Если мы просто уберем этот фильтр, система начнет выдавать по несколько строк на каждую номенклатуру (по одной строке на каждый тип цен). В обычном списке это будет выглядеть как «портянка» данных. Наша задача — заставить «Универсальный отчет» развернуть эти строки в колонки.
Первым делом проанализируем изменения, которые необходимо внести в текст запроса. Нам нужно извлечь поле ТипЦен из регистра и сделать его доступным для построителя отчета. Посмотрим на пример модифицированного запроса, где мы убираем фильтрацию по конкретному типу в параметрах виртуальной таблицы и добавляем выбор типа цен:
ТекстЗапроса = "ВЫБРАТЬ РАЗРЕШЕННЫЕ
| ТаблицаРегистра.Склад КАК Склад,
| ТаблицаРегистра.Номенклатура КАК Номенклатура,
| ТаблицаРегистра.ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
| ТаблицаРегистра.КоличествоОстаток КАК КоличествоОстаток,
| // ... (другие поля остатков)
| ЦеныНоменклатуры.ТипЦен КАК ТипЦен, // Добавляем само поле типа цен
| ВЫБОР
| КОГДА ЦеныНоменклатуры.Цена ЕСТЬ NULL
| ТОГДА ЦеныНоменклатурыБезХарактеристик.Цена * ...
| ИНАЧЕ ЦеныНоменклатуры.Цена * ...
| КОНЕЦ КАК ТипЦенЦена
|ИЗ
| РегистрНакопления.ТоварыНаСкладах.Остатки(&ДатаКон, ) КАК ТаблицаРегистра
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&ДатаКон, ) КАК ЦеныНоменклатуры
| ПО ТаблицаРегистра.Номенклатура = ЦеныНоменклатуры.Номенклатура
| И ТаблицаРегистра.ХарактеристикаНоменклатуры = ЦеныНоменклатуры.ХарактеристикаНоменклатуры";
Важный нюанс: Обратите внимание, что в секции ЛЕВОЕ СОЕДИНЕНИЕ мы убрали условие ТипЦен = &БазовыйТипЦен. Теперь запрос будет выбирать все доступные типы цен для каждой номенклатуры.
Проанализируем работу «Универсального отчета». Чтобы данные распределились по горизонтали, нам необходимо сообщить построителю, что поле ТипЦен является группировкой колонок. В программном коде инициализации отчета это делается следующим образом:
1. Добавим показатель цены в отчет, если он еще не добавлен:
УниверсальныйОтчет.ДобавитьПоказатель("ТипЦенЦена", "Цена", Истина, "ЧЦ=15; ЧДЦ=2", "Цена", "Цена");
2. Укажем системе, что нужно использовать группировку по типу цен в колонках:
УниверсальныйОтчет.ДобавитьГруппировкуКолонок("ТипЦен");
Выясним причину, почему это важно: когда мы добавляем ТипЦен в группировку колонок, «Универсальный отчет» берет все уникальные значения из этого поля и создает для каждого значения свой набор колонок-показателей. Если у вас в базе 3 типа цен, вы увидите 3 колонки «Цена».
При выводе нескольких типов цен возникает критическая опасность: **дублирование остатков**. Поскольку к одной строке остатка теперь «присоединяется» несколько строк из регистра цен, обычное суммирование (СУММА) в итогах по количеству выдаст неверный результат (остаток будет умножен на количество типов цен).
Разберем, как правильно настроить итоги в запросе. Для цен и стоимостей нам нужно использовать агрегатные функции, а для остатков — следить за тем, чтобы они не суммировались по типам цен:
|ИТОГИ
| СУММА(КоличествоОстаток),
| МАКСИМУМ(ТипЦенЦена), // Цену для итогов лучше брать как максимум или среднее
| СУММА(ТипЦенСтоимость)
|ПО
| ОБЩИЕ,
| ТипЦен // Важно добавить группировку по типу цен в итоги
Однако лучший способ управления этим в «Универсальном отчете» — это правильная настройка ресурсов. В настройках построителя необходимо указать, что КоличествоОстаток не должно рассчитываться «в разрезе» ТипЦен. Если отчет строится программно, проверьте секцию {ИТОГИ ПО ...} в тексте запроса — там должны быть перечислены все измерения, по которым допустим расчет ресурсов.
В реальных задачах часто бывает, что цена установлена для номенклатуры в целом, а остатки лежат в разрезе характеристик. Проанализируем конструкцию ВЫБОР КОГДА ... ЕСТЬ NULL, которую использовал автор темы. Это позволяет реализовать «подтягивание» общей цены, если специфичная цена для характеристики не задана:
ВЫБОР
КОГДА ЦеныНоменклатуры.Цена ЕСТЬ NULL
ТОГДА ЕСТЬNULL(ЦеныНоменклатурыБезХарактеристик.Цена, 0)
ИНАЧЕ ЦеныНоменклатуры.Цена
КОНЕЦ КАК ТипЦенЦена
Такой подход гарантирует, что колонка с ценой не будет пустой, если вы ведете учет по характеристикам, но прайс-лист общий для всех видов одного товара.
Если ваши цены хранятся в разных валютах, необходимо привести их к единому знаменателю (например, к валюте управленческого учета). Рассмотрим пример формулы из запроса:
Цена * (КурсВалютыЦены / КратностьВалютыЦены) / (КурсВалютыУпр / КратностьВалютыУпр)
В «Универсальном отчете» такие расчеты лучше выполнять прямо в запросе, соединяясь с виртуальной таблицей КурсыВалют.СрезПоследних. Это позволит пользователю видеть актуальную стоимость остатков в одной валюте, независимо от того, в чем вел закупку поставщик.
Для реализации отчета с несколькими типами цен нам потребовалось:
СрезПоследних.ТипЦен в качестве отдельного измерения в запросе.УниверсальныйОтчет.Используя эти шаги, мы превращаем простой плоский отчет в мощный аналитический инструмент, позволяющий сравнивать разные стоимостные оценки складских запасов в единой таблице.