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