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