Как вывести данные в СКД в виде двух горизонтальных колонок (например, "Приход" и "Расход")?

Программист 1С v8.3 (Управляемые формы)
← На главную

При работе с Системой Компоновки Данных (СКД) в 1С часто возникает задача, когда необходимо представить данные не в виде привычной вертикальной таблицы, а в виде двух или более горизонтальных колонок, которые содержат несвязанные или слабо связанные между собой списки записей. Классический пример — отчет, где "Приход" и "Расход" отображаются параллельно, каждая в своей колонке, как будто это два независимых отчета на одной странице. Стандартные механизмы группировок СКД по умолчанию ориентированы на агрегацию и вертикальное представление данных, что делает такую задачу нетривиальной.

В этой статье мы подробно рассмотрим несколько подходов к решению этой проблемы, начиная с наиболее эффективного для выравнивания детальных записей и заканчивая более сложными или специфичными методами. Мы проанализируем, как можно использовать возможности запросов и настроек СКД, чтобы добиться желаемого результата без постобработки данных вне компоновщика.

Подход 1: Нумерация строк, полное соединение и вывод

Этот подход является наиболее эффективным для вывода неагрегированных детальных записей в две или более горизонтальные колонки, когда требуется "выровнять" независимые строки из разных наборов данных. Он позволяет получить требуемый результат, где каждая строка из одного набора данных сопоставляется со строкой из другого набора.

Разберем принцип: Для того чтобы сопоставить строки "Прихода" со строками "Расхода" в СКД по принципу "одна строка слева — одна строка справа", нам необходимо создать общий "ключ" для сопоставления. Таким ключом будет порядковый номер строки, присвоенный отдельно для каждой из категорий (например, для всех записей прихода и отдельно для всех записей расхода). После присвоения номеров мы сможем "объединить" эти списки горизонтально с помощью полного внешнего соединения по этому порядковому номеру.

  1. Формирование данных для "Прихода" с нумерацией строк.

    Начнем с создания временной таблицы (ВТ), которая будет содержать все необходимые поля для записей прихода, а также дополнительное поле — порядковый номер записи. Мы используем функцию ROW_NUMBER() для нумерации.

    
    ВЫБРАТЬ
        ИсходныеДанные.Дата КАК ДатаПрихода,
        ИсходныеДанные.Регистратор КАК РегистраторПрихода,
        ИсходныеДанные.Количество КАК КоличествоПрихода,
        ROW_NUMBER() ПЕРЕГРАДИТЬ УПОРЯДОЧИТЬ ПО ИсходныеДанные.Дата, ИсходныеДанные.Регистратор КАК НомерСтроки
    ПОМЕСТИТЬ ВТ_Приход
    ИЗ
        РегистрНакопления.ОстаткиИОбороты.Обороты(
            &НачалоПериода,
            &КонецПериода,
            Регистратор,
            ,
            Приход = ИСТИНА
        ) КАК ИсходныеДанные
    ;
    

    Здесь ROW_NUMBER() ПЕРЕГРАДИТЬ УПОРЯДОЧИТЬ ПО ... — это функция нумерации строк, которая присваивает уникальный порядковый номер каждой строке в рамках заданного упорядочивания. В нашем случае мы упорядочиваем по Дата и Регистратор, чтобы обеспечить стабильный и предсказуемый порядок нумерации. ПОМЕСТИТЬ ВТ_Приход создает временную таблицу с этими данными, которая будет использоваться на следующем шаге.

  2. Формирование данных для "Расхода" с нумерацией строк.

    Аналогичным образом мы создаем временную таблицу для записей расхода, присваивая им собственные порядковые номера, независимые от номеров прихода.

    
    ВЫБРАТЬ
        ИсходныеДанные.Дата КАК ДатаРасход,
        ИсходныеДанные.Регистратор КАК РегистраторРасход,
        ИсходныеДанные.Количество КАК КоличествоРасход,
        ROW_NUMBER() ПЕРЕГРАДИТЬ УПОРЯДОЧИТЬ ПО ИсходныеДанные.Дата, ИсходныеДанные.Регистратор КАК НомерСтроки
    ПОМЕСТИТЬ ВТ_Расход
    ИЗ
        РегистрНакопления.ОстаткиИОбороты.Обороты(
            &НачалоПериода,
            &КонецПериода,
            Регистратор,
            ,
            Приход = ЛОЖЬ
        ) КАК ИсходныеДанные
    ;
    

    Этот шаг идентичен предыдущему, но выполняется для данных, где поле Приход имеет значение ЛОЖЬ. В результате мы получаем две временные таблицы: ВТ_Приход и ВТ_Расход, каждая со своим набором данных и собственной сквозной нумерацией строк.

  3. Полное соединение временных таблиц.

    Теперь, когда у нас есть две временные таблицы с одинаковой структурой поля НомерСтроки, мы можем выполнить полное внешнее соединение (ПОЛНОЕ СОЕДИНЕНИЕ) по этому полю. Это позволит нам получить все строки из обеих таблиц, сопоставив их по номеру. Если для какого-то номера строки есть запись только в одной из таблиц, поля из отсутствующей таблицы будут заполнены значением NULL.

    
    ВЫБРАТЬ
        ВТ_Приход.ДатаПрихода,
        ВТ_Приход.РегистраторПрихода,
        ВТ_Приход.КоличествоПрихода,
        ВТ_Расход.ДатаРасход,
        ВТ_Расход.РегистраторРасход,
        ВТ_Расход.КоличествоРасход
    ИЗ
        ВТ_Приход КАК ВТ_Приход
            ПОЛНОЕ СОЕДИНЕНИЕ ВТ_Расход КАК ВТ_Расход
            ПО ВТ_Приход.НомерСтроки = ВТ_Расход.НомерСтроки
    УПОРЯДОЧИТЬ ПО
        ВТ_Приход.НомерСтроки, ВТ_Расход.НомерСтроки
    

    Оператор ПОЛНОЕ СОЕДИНЕНИЕ здесь является критически важным, поскольку он гарантирует, что мы увидим все строки как из "Прихода", так и из "Расхода", даже если количество записей в этих категориях различается. Если для определенного НомерСтроки нет соответствующей записи, например, в ВТ_Расход, то все поля из ВТ_Расход для этой строки будут иметь значение NULL, но строка "Прихода" все равно будет выведена. Упорядочивание по НомерСтроки обеспечивает логичный порядок вывода.

Настройка СКД для вывода результата:

  1. В выбранных полях СКД укажите все поля, полученные из финального запроса: ДатаПрихода, РегистраторПрихода, КоличествоПрихода, ДатаРасход, РегистраторРасход, КоличествоРасход.

  2. Структура отчета: достаточно добавить детальные записи. Группировка по НомерСтроки не требуется, так как соединение уже выровняло строки, и каждая строка в результате запроса уже содержит горизонтально сопоставленные данные.

  3. Для заголовков колонок можно использовать пользовательские макеты полей или просто переименовать поля в настройках СКД для лучшей читаемости, например, "Дата (Приход)" и "Дата (Расход)".

Преимущества этого подхода:

Недостатки этого подхода:

Подход 2: Объединение наборов данных с "разверткой" в колонки через вычисляемые поля

Этот метод отличается от предыдущего и имеет свои области применения. Он менее подходит для *детальных* записей, которые нужно выровнять по горизонтали как независимые списки, но отлично работает для агрегированных данных или когда требуется вывести одни и те же поля, но с разными значениями в зависимости от условия, при этом каждая исходная строка выводится в отдельной строке отчета.

Разберем принцип: Мы объединяем все исходные данные (Приход и Расход) в одном запросе с помощью оператора ОБЪЕДИНИТЬ ВСЕ, добавляя к каждой строке флаг, указывающий на тип операции (Приход или Расход). Затем в СКД создаем вычисляемые поля, которые будут принимать значение только для соответствующего типа операции, а для других типов — значение NULL (пустое).

  1. Объединение запросов с флагом операции.

    Сначала мы формируем единый набор данных, в котором каждая запись содержит информацию о своей принадлежности к "Приходу" или "Расходу" через специальное поле-флаг.

    
    ВЫБРАТЬ
        ИсходныеДанные.Дата КАК Дата,
        ИсходныеДанные.Регистратор КАК Регистратор,
        ИсходныеДанные.Количество КАК Количество,
        ИСТИНА КАК ЭтоПриход
    ИЗ
        РегистрНакопления.ОстаткиИОбороты.Обороты(
            &НачалоПериода,
            &КонецПериода,
            Регистратор,
            ,
            Приход = ИСТИНА
        ) КАК ИсходныеДанные
    
    ОБЪЕДИНИТЬ ВСЕ
    
    ВЫБРАТЬ
        ИсходныеДанные.Дата,
        ИсходныеДанные.Регистратор,
        ИсходныеДанные.Количество,
        ЛОЖЬ КАК ЭтоПриход
    ИЗ
        РегистрНакопления.ОстаткиИОбороты.Обороты(
            &НачалоПериода,
            &КонецПериода,
            Регистратор,
            ,
            Приход = ЛОЖЬ
        ) КАК ИсходныеДанные
    

    В этом запросе мы используем ОБЪЕДИНИТЬ ВСЕ, чтобы собрать все записи прихода и расхода в один список. Важно, что мы добавляем к каждой записи поле ЭтоПриход, которое принимает значение ИСТИНА для прихода и ЛОЖЬ для расхода. Это поле станет основой для дальнейшей "развертки" в колонки.

  2. Создание вычисляемых полей в СКД.

    После получения объединенного набора данных, переходим на вкладку "Вычисляемые поля" в настройках СКД и создаем новые поля, которые будут выводить данные только для своей категории:

    
    ДатаПрихода = ВЫБОР КОГДА ЭтоПриход ТОГДА Дата ИНАЧЕ NULL КОНЕЦ
    РегистраторПрихода = ВЫБОР КОГДА ЭтоПриход ТОГДА Регистратор ИНАЧЕ NULL КОНЕЦ
    КоличествоПрихода = ВЫБОР КОГДА ЭтоПриход ТОГДА Количество ИНАЧЕ NULL КОНЕЦ
    
    ДатаРасход = ВЫБОР КОГДА НЕ ЭтоПриход ТОГДА Дата ИНАЧЕ NULL КОНЕЦ
    РегистраторРасход = ВЫБОР КОГДА НЕ ЭтоПриход ТОГДА Регистратор ИНАЧЕ NULL КОНЕЦ
    КоличествоРасход = ВЫБОР КОГДА НЕ ЭтоПриход ТОГДА Количество ИНАЧЕ NULL КОНЕЦ
    

    Эти выражения используют оператор ВЫБОР КОГДА для условного заполнения полей. Если строка относится к "Приходу" (ЭтоПриход = ИСТИНА), то заполняются поля ДатаПрихода, РегистраторПрихода, КоличествоПрихода. В противном случае (для расхода) они будут NULL. Аналогично для расходных полей.

  3. Настройка структуры отчета.

    В структуру отчета (например, в детальные записи или в группировку по дате) добавьте эти новые вычисляемые поля. СКД выведет все строки из объединенного набора данных. В колонках "Прихода" будут значения только для приходов, а для расходов там будут пустоты. Наоборот, в колонках "Расхода" будут значения только для расходов.

Преимущества этого подхода:

Недостатки этого подхода:

Подход 3: Использование элемента "Таблица" в структуре отчета СКД

Элемент "Таблица" в структуре отчета СКД предназначен в первую очередь для построения кросс-отчетов (перекрестных таблиц), где данные агрегируются как по строкам, так и по колонкам. Его основное назначение — выводить агрегированные значения на пересечении группировок. Хотя его можно адаптировать, для вывода неагрегированных детальных записей в две колонки он подходит со значительными оговорками.

Разберем принцип: Мы можем создать группировку по колонкам, которая будет зависеть от типа операции ("Приход" или "Расход"). СКД попытается вывести данные для каждой категории в свою колонку. Однако, по умолчанию "Таблица" ожидает агрегированных значений (ресурсов) на пересечении группировок строк и колонок, а не просто детальные записи.

  1. Добавление элемента "Таблица".

    В структуре отчета СКД добавьте новый элемент "Таблица".

  2. Настройка группировок строк.

    Если вам нужна какая-либо общая группировка по строкам (например, по номенклатуре, или по периоду), добавьте ее в "Группировки строк" элемента "Таблица". Если вам нужны просто детальные записи без общей группировки, то это усложняет использование "Таблицы", так как для детальных записей без агрегации она не так эффективна.

  3. Настройка группировок колонок.

    Создайте новую группировку в "Группировки колонок". В качестве поля для группировки выберите поле, которое различает "Приход" и "Расход". Это может быть поле Приход из исходных данных или созданное вами поле-флаг, например, ЭтоПриход из Подхода 2.

  4. Размещение полей и ресурсов.

    Внутри этой колонковой группировки можно попытаться вывести необходимые поля: Дата, Регистратор, Количество. Однако, если вы просто добавите эти поля, СКД, скорее всего, попытается их агрегировать (например, вывести первое значение, или выдаст ошибку, если нет ресурса). Чтобы вывести детальные записи, вам придется добавить их в "Детальные записи" или определить их как ресурсы с функцией "Первое" / "Последнее", что не всегда корректно для списка.

  5. Настройка макета при необходимости.

    Для того чтобы вывести несколько детальных записей в ячейке на пересечении, часто требуется очень сложная настройка пользовательских макетов для ячеек "Таблицы", что практически равносильно реализации Подхода 4.

Преимущества этого подхода:

Недостатки этого подхода:

Подход 4: Расширенное использование пользовательских макетов СКД

Пользовательские макеты предоставляют максимальный контроль над отображением данных в СКД, позволяя создавать отчеты с уникальной структурой, которая может быть недостижима стандартными средствами. Этот подход является самым гибким, но и самым трудоемким.

Разберем принцип: Вы можете создать макет для детальных записей или для определенной группировки, который будет содержать две независимые области. В каждую область вы сможете поместить параметры, соответствующие полям для "Прихода" и "Расхода" соответственно. Этот метод подразумевает, что данные для горизонтального вывода уже подготовлены в запросе (как в Подходе 1).

  1. Подготовка данных в запросе.

    Для этого подхода вам, скорее всего, потребуется предварительно подготовить данные, как это было описано в Подходе 1 (нумерация строк и полное соединение). Это обеспечит наличие выровненных по горизонтали наборов полей для каждой строки отчета.

  2. Создание пользовательского макета.

    Перейдите на вкладку "Макеты" в настройках СКД. Выберите тип макета, который вы хотите переопределить — например, "Макет детальных записей" или "Макет группировки" (если вы используете общую группировку). Создайте новый макет, нажав кнопку "Добавить".

  3. Определение областей в макете.

    В открывшемся конструкторе макета вы можете создать несколько областей (например, "ЛеваяЧасть" и "ПраваяЧасть"), расположенных горизонтально. Для этого выделите необходимые ячейки и используйте команду "Таблица > Объединить ячейки" или просто разместите элементы в нужных местах.

  4. Размещение параметров в областях.

    В каждую созданную область поместите параметры, соответствующие полям из вашего запроса. Например, в "ЛевуюЧасть" поместите параметры [ДатаПрихода], [РегистраторПрихода], [КоличествоПрихода]. В "ПравуюЧасть" — [ДатаРасход], [РегистраторРасход], [КоличествоРасход]. Для этого в ячейках макета нужно просто набрать текст вида [ИмяПоля].

  5. Связывание макета.

    После создания макета вернитесь в структуру отчета. В свойствах соответствующего элемента (детальные записи или группировки) найдите свойство "Макет" и выберите созданный вами пользовательский макет.

  6. Обработка NULL значений.

    В макете можно настроить условное форматирование или даже скрытие областей, если все поля в одной из сторон (например, Расход) имеют NULL значения, что указывает на отсутствие данных в этой части для текущей строки. Это делается через свойства областей макета (например, "Условие вывода").

Преимущества этого подхода:

Недостатки этого подхода:

Подход 5: Дублирование полей в запросе для отдельных колонок (не для выравнивания строк)

Этот подход является скорее уточнением, чем полноценным решением вашей задачи горизонтального выравнивания независимых списков. Он актуален, когда вы хотите вывести одно и то же поле (например, "Дата") несколько раз в разных колонках отчета, но с разным контекстом или псевдонимом. Однако, он совершенно не решает проблему горизонтального выравнивания *разных* строк "Прихода" и "Расхода" по их номерам или другим ключам.

Разберем принцип: Если СКД по умолчанию "схлопывает" одноименные поля в одну колонку отчета (что происходит, когда вы, например, дважды добавляете одно и то же поле "Дата" в выбранные поля), мы можем дать этим полям разные псевдонимы прямо в запросе. Таким образом, СКД будет воспринимать их как совершенно разные поля и выведет их в отдельных колонках.

  1. Пример запроса с дублированием полей:

    
    ВЫБРАТЬ
        Дата КАК ДатаПрихода,
        Регистратор КАК РегистраторПрихода,
        Количество КАК КоличествоПрихода,
        Дата КАК ДатаРасход, // Дублируем поле Дата, давая новый псевдоним
        Регистратор КАК РегистраторРасход, // Дублируем поле Регистратор
        Количество КАК КоличествоРасход // Дублируем поле Количество
    ИЗ
        ВашаИсходнаяТаблица
    

    В данном примере, если ВашаИсходнаяТаблица содержит смешанные данные прихода и расхода, то каждая строка этой таблицы будет выведена в отчете как одна запись, но с дублирующимися колонками ДатаПрихода и ДатаРасход (и аналогично для других полей). Обе колонки будут содержать одинаковые значения из исходной строки, или же потребуется более сложная логика в запросе для условного заполнения этих продублированных полей.

Преимущества этого подхода:

Недостатки этого подхода:

Выбор оптимального подхода

После рассмотрения различных методов, мы можем сделать следующие выводы о выборе оптимального решения для вашей задачи:

Важно: Всегда тщательно тестируйте разработанные отчеты, особенно при использовании сложных запросов и макетов. Убедитесь в корректности вывода данных, их соответствии требованиям и ожидаемой производительности на реальных объемах информации.

← На главную