При работе с системой 1С:Предприятие часто возникает задача по формированию сложных аналитических отчетов по данным конкретного открытого документа. При этом отчет должен использовать возможности Системы Компоновки Данных (СКД) для группировок, итогов и красивого оформления, а также учитывать специфический, программно заданный отбор по данным табличной части документа. Рассмотрим, как эффективно решить эту задачу.
Мы выясним, как передать ссылку на текущий документ в отчет СКД, реализовать сложные программные отборы, подобные тем, что используются при отображении данных в форме документа, и затем сформировать полноценный отчет с необходимой детализацией и группировками. Разберем различные подходы, подходящие для разных уровней сложности отбора.
Первым шагом в формировании отчета по текущему документу является передача его идентификатора, то есть ссылки, в параметры отчета СКД. СКД оперирует данными, хранящимися в базе, поэтому нам нужна именно ссылка, а не объект документа, который может быть еще не записан.
Создание параметра в схеме компоновки данных (СКД):
В конфигураторе, при создании или редактировании схемы компоновки данных вашего отчета (иногда требуется использовать две разных схемы в отчете СКД), перейдите на вкладку "Параметры". Создайте новый параметр, например, с именем СсылкаДокумента. Установите тип параметра, соответствующий типу вашего документа (например, ДокументСсылка.МойДокумент). Этот параметр будет служить для отбора данных по конкретному документу.
Использование параметра в запросе набора данных СКД:
На вкладке "Наборы данных" схемы компоновки данных создайте "Набор данных - Запрос" (в этом поможет управляемая консоль запросов). В запросе необходимо использовать параметр &СсылкаДокумента в условии ГДЕ для отбора строк табличной части, принадлежащих выбранному документу. Предположим, ваш документ называется "МойДокумент", а его табличная часть - "ТабличнаяЧасть".
ВЫБРАТЬ
МойДокументТабличнаяЧасть.Поле1 КАК Поле1,
МойДокументТабличнаяЧасть.Поле2 КАК Поле2,
МойДокументТабличнаяЧасть.Поле3 КАК Поле3,
МойДокументТабличнаяЧасть.Ссылка КАК СсылкаНаДокумент
ИЗ
Документ.МойДокумент.ТабличнаяЧасть КАК МойДокументТабличнаяЧасть
ГДЕ
МойДокументТабличнаяЧасть.Ссылка = &СсылкаДокумента
Таким образом, мы гарантируем, что запрос выберет данные только из табличной части того документа, ссылку на который мы передадим.
Программная передача значения параметра:
Когда пользователь вызывает отчет из формы документа, нам необходимо установить значение параметра СсылкаДокумента. Обычно это делается в модуле формы или в модуле команды, которая вызывает отчет. Получите объект компоновщика настроек отчета и установите значение параметра:
// Предположим, что ТекущийДокумент - это ссылка на документ,
// который открыт или по которому вызывается отчет.
// Например, это может быть Объект.Ссылка в модуле формы документа.
КомпоновщикНастроек = ОтчетОбъект.КомпоновщикНастроек; // ОтчетОбъект - это объект вашего отчета
ПараметрДанных = КомпоновщикНастроек.Настройки.ПараметрыДанных.НайтиЗначениеПараметра(Новый ПараметрКомпоновкиДанных("СсылкаДокумента"));
Если ПараметрДанных <> Неопределено Тогда
ПараметрДанных.Значение = ТекущийДокумент;
ПараметрДанных.Использование = Истина;
КонецЕсли;
// Для формирования отчета
// ОтчетОбъект.СкомпоноватьРезультат(Элементы.ПолеТабличногоДокумента);
Мы получаем доступ к настройкам отчета через КомпоновщикНастроек, находим наш параметр СсылкаДокумента и присваиваем ему значение ссылки на текущий документ. После этого отчет будет сформирован только по данным указанного документа.
Теперь рассмотрим, как реализовать программный отбор, подобный тому, что был представлен в сообщении 18. Этот отбор достаточно сложен: он включает проверку начала наименования номенклатуры, наличие спецификации, и вложенный цикл по комплектующим спецификации с дополнительными условиями. Подобные отборы могут быть реализованы в СКД несколькими способами, выбор которых зависит от сложности логики.
Если логика отбора выражается простыми условиями, которые можно перевести на язык запросов 1С (например, ЛЕВ(Наименование, 2) = "ВП"), то мы можем включить эти условия непосредственно в запрос набора данных СКД. Значения для таких условий можно передавать через параметры.
Например, для условия Лев(СтрокаПродукции.Номенклатура.Наименование, 2) = "ВП":
Добавьте условие в запрос СКД:
ВЫБРАТЬ
МойДокументТабличнаяЧасть.Поле1 КАК Поле1,
МойДокументТабличнаяЧасть.Поле2 КАК Поле2,
МойДокументТабличнаяЧасть.Поле3 КАК Поле3,
МойДокументТабличнаяЧасть.Номенклатура.Наименование КАК НаименованиеНоменклатуры
ИЗ
Документ.МойДокумент.ТабличнаяЧасть КАК МойДокументТабличнаяЧасть
ГДЕ
МойДокументТабличнаяЧасть.Ссылка = &СсылкаДокумента
И ПОДСТРОКА(МойДокументТабличнаяЧасть.Номенклатура.Наименование, 1, 2) = "ВП"
Мы использовали функцию ПОДСТРОКА, аналогичную ЛЕВ, чтобы применить условие непосредственно в запросе. Однако, если условий много и они динамически меняются, такой подход становится менее гибким (например, если требуется сравнение строки с представлением ссылки в запросе).
Более гибкий способ для программного применения отборов, когда условия могут зависеть от внешней логики, — это динамическое добавление элементов отбора в объект КомпоновщикНастроек отчета. Этот метод позволяет строить сложные цепочки условий (с использованием И / ИЛИ).
Рассмотрим, как добавить простое условие "Наименование номенклатуры начинается с 'ВП'":
Получение объекта КомпоновщикНастроек:
В модуле отчета или формы, перед формированием отчета, получите доступ к текущим настройкам отчета.
// ОтчетОбъект - это объект вашего отчета
Настройки = ОтчетОбъект.КомпоновщикНастроек.Настройки;
Создание и добавление элемента отбора:
Мы создаем новый элемент отбора, указываем поле, вид сравнения и значение.
// Создаем новый элемент отбора
НовыйЭлементОтбора = Настройки.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
НовыйЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Номенклатура.Наименование"); // Поле, по которому отбираем. Убедитесь, что оно есть в доступных полях СКД.
НовыйЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.НачинаетсяС; // Вид сравнения (НачинаетсяС, Равно, ВСписке и т.д.)
НовыйЭлементОтбора.ПравоеЗначение = "ВП"; // Значение для сравнения
НовыйЭлементОтбора.Использование = Истина; // Включаем отбор
Для объединения нескольких условий (например, "ИЛИ") можно использовать ГруппаЭлементовОтбораКомпоновкиДанных:
ГруппаОтбора = Настройки.Отбор.Элементы.Добавить(Тип("ГруппаЭлементовОтбораКомпоновкиДанных"));
ГруппаОтбора.ТипГруппы = ТипГруппыЭлементовОтбораКомпоновкиДанных.ИЛИ; // Или И
ГруппаОтбора.Использование = Истина;
Элемент1 = ГруппаОтбора.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
Элемент1.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Номенклатура.Наименование");
Элемент1.ВидСравнения = ВидСравненияКомпоновкиДанных.НачинаетсяС;
Элемент1.ПравоеЗначение = "Диафрагма вулканизационная";
Элемент1.Использование = Истина;
Элемент2 = ГруппаОтбора.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
Элемент2.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Номенклатура.Наименование");
Элемент2.ВидСравнения = ВидСравненияКомпоновкиДанных.НачинаетсяС;
Элемент2.ПравоеЗначение = "ВД";
Элемент2.Использование = Истина;
Таким образом, мы можем программно сконструировать достаточно сложные условия отбора, но это применимо, когда логика отбора может быть выражена стандартными операциями СКД по полям из запроса.
Проанализируем специфический отбор, описанный в сообщении 18. Он включает в себя:
Лев(СтрокаПродукции.Номенклатура.Наименование, 2) = "ВП").СтрокаПродукции.Спецификация = Неопределено).Для каждого Комплектующее Из СтрокаПродукции.Спецификация.ИсходныеКомплектующие Цикл).Лев().Прервать).Подобная логика, особенно с вложенными циклами и проверками через ссылки на другие объекты (спецификация, комплектующие), крайне затруднительна, а порой и невозможна для прямой реализации в запросе СКД или через стандартные элементы отбора. Язык запросов 1С не поддерживает циклы, а сложные условия с доступом к коллекциям в связанных объектах выходят за рамки его возможностей.
В таких случаях наиболее эффективным решением будет предварительная программная обработка данных. Мы будем использовать подход "Набор данных - Объект" в СКД.
Мы должны программно получить табличную часть документа, применить к ней всю необходимую сложную логику отбора на встроенном языке 1С, а затем передать уже отфильтрованные данные в СКД. Мы создаем временную таблицу значений, в которую записываем только те строки, которые соответствуют нашему сложному отбору.
Адаптируем ваш код из сообщения 18:
// ВАЖНО: Это выполняется в модуле формы или команды,
// которая вызывает отчет, ДО вызова СКД.
// Получаем объект текущего документа по ссылке
// Например: ТекущийДокументОбъект = ТекущийДокумент.ПолучитьОбъект();
// Если ТекущийДокумент - это уже Объект в форме документа
// (например, Объект в модуле объекта или формы), то используем его напрямую.
// Предположим, что ТекущийДокументОбъект - это переменная типа ДокументОбъект.
// Например, если код в модуле формы документа, то это будет "ЭтотОбъект" или "Объект".
// Создадим временную таблицу значений для отфильтрованных данных
ТаблицаДляОтчета = Новый ТаблицаЗначений;
ТаблицаДляОтчета.Колонки.Добавить("Номенклатура");
ТаблицаДляОтчета.Колонки.Добавить("Характеристика");
ТаблицаДляОтчета.Колонки.Добавить("Количество");
// Добавьте все колонки, которые вам нужны в отчете из ТЧ документа
Если ТекущийДокументОбъект.Продукция.Количество() > 0 Тогда
Для Каждого СтрокаПродукции Из ТекущийДокументОбъект.Продукция Цикл
ПоказыватьСтроку = Ложь;
// Применяем первую часть отбора: Наименование номенклатуры
Если Лев(СтрокаПродукции.Номенклатура.Наименование, 2) = "ВП" Тогда
Если СтрокаПродукции.Спецификация = Неопределено Тогда
ПоказыватьСтроку = Истина;
Иначе // Спецификация определена, проверяем ее комплектующие
ПоказыватьСтроку = Истина; // Изначально показываем, если не найдем "запрещенных" комплектующих
Для каждого Комплектующее Из СтрокаПродукции.Спецификация.ИсходныеКомплектующие Цикл
НаименованиеКомплектующего = Комплектующее.Номенклатура.Наименование;
Если Найти(НаименованиеКомплектующего, "Диафрагма вулканизационная") = 1 ИЛИ
Найти(НаименованиеКомплектующего, "ВД") = 1 ИЛИ
Найти(НаименованиеКомплектующего, "Диафрагма ""10"" х 355") = 1 ИЛИ
Найти(НаименованиеКомплектующего, "Диафрагма ""9"" х 355") = 1 ИЛИ
Найти(НаименованиеКомплектующего, "Диафрагма 11""х345") = 1 ИЛИ
Найти(НаименованиеКомплектующего, "Диафрагма ""12"" х 370") = 1 ИЛИ
Найти(НаименованиеКомплектующего, "Диафрагма 12х340") = 1 ИЛИ
Найти(НаименованиеКомплектующего, "Вулканизационные диафрагмы") = 1 Тогда
ПоказыватьСтроку = Ложь; // Найдено запрещенное комплектующее
Прервать; // Выходим из цикла по комплектующим
КонецЕсли;
КонецЦикла;
КонецЕсли;
КонецЕсли;
// Если строка соответствует всем условиям, добавляем ее в таблицу для отчета
Если ПоказыватьСтроку Тогда
НоваяСтрока = ТаблицаДляОтчета.Добавить();
НоваяСтрока.Номенклатура = СтрокаПродукции.Номенклатура;
НоваяСтрока.Характеристика = СтрокаПродукции.Характеристика; // Пример других полей
НоваяСтрока.Количество = СтрокаПродукции.Количество;
// Заполните остальные необходимые поля
КонецЕсли;
КонецЦикла;
КонецЕсли;
Мы адаптировали ваш код, чтобы он не просто устанавливал флаг Показывать, а формировал новую ТаблицуЗначений с уже отфильтрованными данными. Обратите внимание, что мы используем Найти() = 1 вместо Лев(), чтобы не зависеть от жесткой длины подстроки, если в оригинальном коде была опечатка (Лев(..., 26) - слишком много для "Диафрагма вулканизационная"). Для точного сравнения по началу строки, используем Найти(НаименованиеКомплектующего, "Диафрагма вулканизационная") = 1.
Теперь, когда у нас есть отфильтрованная ТаблицаДляОтчета, мы передадим ее в СКД.
В схеме компоновки данных: На вкладке "Наборы данных" создайте "Набор данных - Объект". В поле "Имя объекта" укажите имя, например, ДанныеТабличнойЧасти. В поле "Тип объекта" выберите ТаблицаЗначений. На вкладке "Поля" в этом наборе данных вручную добавьте все колонки, которые вы добавляли в вашу ТаблицаДляОтчета (например, "Номенклатура", "Характеристика", "Количество").
Программная передача таблицы в СКД: После того как вы сформировали ТаблицаДляОтчета, ее нужно передать в компоновщик настроек отчета.
// КомпоновщикНастроек - объект КомпоновщикНастроек
// ТаблицаДляОтчета - наша отфильтрованная ТаблицаЗначений
КомпоновщикНастроек.НаборыДанных.ДанныеТабличнойЧасти.УстановитьОбъект(ТаблицаДляОтчета);
// Далее формируем отчет стандартным способом
// Например: ОтчетОбъект.СкомпоноватьРезультат(Элементы.ПолеТабличногоДокумента);
Этот метод позволяет полностью использовать всю мощь встроенного языка 1С для предварительной обработки и фильтрации данных, а затем уже использовать СКД для их представления, группировки и вывода итогов.
СКД изначально создана для построения сложных аналитических отчетов. После того как вы обеспечили выборку и отбор данных одним из описанных выше способов, вы можете легко настроить внешний вид и функциональность отчета.
Настройка группировок:
В схеме компоновки данных, на вкладке "Настройки" (чаще всего "Настройки по умолчанию"), перейдите на вкладку "Структура". Здесь вы можете добавить нужные группировки. Например, если вы хотите сгруппировать данные по номенклатуре, добавьте новую группировку и выберите поле "Номенклатура". Вы можете создавать вложенные группировки для более детальной аналитики.
Добавление итогов (ресурсов):
На той же вкладке "Настройки" в разделе "Ресурсы" укажите поля, по которым необходимо рассчитывать итоги. Например, для поля "Количество" вы можете выбрать функции Сумма(), Среднее(), Количество() и другие. СКД автоматически рассчитает эти итоги по каждой группировке и по отчету в целом.
Определение выводимых колонок:
На вкладке "Выбранные поля" в настройках отчета укажите, какие три колонки (или любое другое количество) вы хотите видеть в отчете. Например, "Номенклатура", "Характеристика", "Количество". Вы можете перетаскивать поля, менять их порядок и задавать заголовки.
Расширенные возможности:
СКД также позволяет настраивать условное оформление (например, выделять строки определенного цвета), создавать вычисляемые поля (например, "Сумма * Количество") и добавлять параметры, которые пользователь может изменять интерактивно.
В процессе обсуждения неоднократно возникал вопрос о разнице между "ссылкой" и "объектом" документа. Давайте еще раз проясним этот момент, так как он критичен для понимания работы с данными 1С:
ДокументСсылка: Это уникальный идентификатор (GUID) документа, который уже сохранен в базе данных. Когда мы передаем ссылку в СКД, СКД использует язык запросов для обращения к данным, которые *уже существуют* в таблицах базы данных. Запросы СКД работают только со ссылками на сохраненные объекты.
ДокументОбъект: Это экземпляр документа, который находится в оперативной памяти программы. Объект может быть новым (еще не записанным в базу), или прочитанным из базы для редактирования. Данные объекта доступны для изменения на встроенном языке 1С, включая обход табличных частей в циклах и вызов методов. Если документ новый или его данные еще не записаны (например, вы только что добавили строки в табличную часть, но не нажали "Записать"), то по его ссылке в базе этих данных еще не будет. Поэтому, если нам нужна логика, которая оперирует именно с текущим состоянием объекта (даже если он не записан), мы сначала получаем ДокументОбъект (например, Объект в модуле формы документа), обрабатываем его данные, и только потом, если необходимо, передаем результат в СКД через "Набор данных - Объект".
В нашем случае, когда речь идет о "текущем открытом документе с заполненными данными табличной части", мы подразумеваем, что документ может быть как сохраненным, так и находиться в процессе редактирования. Если данные еще не сохранены, прямое использование запроса СКД по ссылке на документ покажет старые данные (или ничего, если документ новый). Поэтому для обработки *текущих, возможно несохраненных* данных объекта с применением сложной логики отбора, метод "Набор данных - Объект" с предварительной программной обработкой является наилучшим выбором.
Таким образом, мы рассмотрели комплексный подход к формированию отчета на СКД по табличной части текущего документа, учитывая специфические программные отборы. Выбор конкретного метода реализации отбора будет зависеть от его сложности и требований к производительности.