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