Как разработать кросс-отчет (шахматку) по остаткам без использования СКД и построителей?

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

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

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

Постановка задачи и основные ограничения

Рассмотрим ситуацию: нам необходимо вывести остатки товаров. Вниз по вертикали должна идти «Номенклатура», вправо по горизонтали — «Склады». Отчет должен формироваться на определенную дату с учетом отборов. Главное условие — полный запрет на СКД, Построитель отчета и Построитель запроса. Также важно соблюсти условие: если по складу нет остатка по выбранным товарам, колонка с этим складом не должна отображаться.

Шаг 1: Получение данных через запрос

Первым делом нам необходимо получить сырые данные. Поскольку использование построителей запрещено, мы напишем классический запрос к виртуальной таблице остатков. Выясним причину, почему важно правильно сгруппировать данные: нам нужны уникальные пары «Номенклатура - Склад» и их количественный итог.

Проанализируем пример запроса для получения данных из регистра ТоварыНаСкладах:


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

Здесь крайне важно использовать УПОРЯДОЧИТЬ ПО, так как это упростит нам дальнейший обход данных при формировании строк табличного документа.

Шаг 2: Подготовка структуры колонок

Поскольку мы строим шахматку, нам нужно заранее знать, какие склады будут выступать в качестве заголовков колонок. Разберем по шагам, как получить этот список:

  1. Выгрузим результат запроса в объект ТаблицаЗначений.
  2. Создадим отдельную таблицу или массив для хранения уникальных складов.
  3. Используем метод Скопировать() и Свернуть(), чтобы оставить только уникальные наименования складов, по которым реально есть остатки.

Рассмотрим фрагмент кода для подготовки списка колонок:


ТаблицаДанных = Запрос.Выполнить().Выгрузить();

ТаблицаСкладов = ТаблицаДанных.Скопировать();
ТаблицаСкладов.Свернуть("Склад"); // Получаем уникальные склады
МассивСкладов = ТаблицаСкладов.ВыгрузитьКолонку("Склад");

Шаг 3: Работа с макетом и вывод шапки

Для вывода отчета нам понадобится макет. В нем мы создадим три ключевые области:

Приступим к формированию шапки. Посмотрим на алгоритм: сначала мы используем метод Вывести() для первой колонки, а затем в цикле по массиву складов используем метод Присоединить().


ТабДок = Новый ТабличныйДокумент;
Макет = ПолучитьМакет("Макет");

// Выводим левый верхний угол
Область = Макет.ПолучитьОбласть("Шапка|Номенклатура");
ТабДок.Вывести(Область);

// Присоединяем названия складов вправо
ОбластьСклад = Макет.ПолучитьОбласть("Шапка|Склад");
Для Каждого ТекСклад Из МассивСкладов Цикл
    ОбластьСклад.Параметры.ИмяСклада = ТекСклад;
    ТабДок.Присоединить(ОбластьСклад);
КонецЦикла;

Шаг 4: Формирование строк и заполнение данными

Это самый ответственный этап. Нам нужно пройтись по всем товарам, и для каждого товара «пробежаться» по всем складам, чтобы поставить число в нужную колонку. Если данных по конкретной паре «Товар-Склад» в нашей таблице нет, мы должны вывести ноль или пустоту.

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

Рассмотрим пример обхода с использованием ТаблицыЗначений и метода НайтиСтроки():


ТаблицаДанных.Индексы.Добавить("Номенклатура, Склад");

// Получаем список уникальной номенклатуры
ТаблицаНоменклатуры = ТаблицаДанных.Скопировать();
ТаблицаНоменклатуры.Свернуть("Номенклатура");

Для Каждого СтрНоменклатура Из ТаблицаНоменклатуры Цикл
    
    // Выводим начало строки (название товара)
    ОбластьСтрока = Макет.ПолучитьОбласть("Строка|Номенклатура");
    ОбластьСтрока.Параметры.Товар = СтрНоменклатура.Номенклатура;
    ТабДок.Вывести(ОбластьСтрока);
    
    // Вложенный цикл по ВСЕМ колонкам-складам
    ОбластьДанные = Макет.ПолучитьОбласть("Строка|Склад");
    Для Каждого ТекСклад Из МассивСкладов Цикл
        
        Отбор = Новый Структура;
        Отбор.Вставить("Номенклатура", СтрНоменклатура.Номенклатура);
        Отбор.Вставить("Склад", ТекСклад);
        
        НайденныеСтроки = ТаблицаДанных.НайтиСтроки(Отбор);
        
        Если НайденныеСтроки.Количество() > 0 Тогда
            ОбластьДанные.Параметры.Количество = НайденныеСтроки[0].Количество;
        Иначе
            ОбластьДанные.Параметры.Количество = 0;
        КонецЕсли;
        
        ТабДок.Присоединить(ОбластьДанные);
        
    КонецЦикла;
    
КонецЦикла;

Шаг 5: Оптимизация и тонкие моменты

Рассмотрим подробнее вопрос производительности. Если номенклатуры тысячи, а складов сотни, метод НайтиСтроки() может замедлить работу. В этом случае эффективно использовать Соответствие (Map). Мы можем создать ключ вида Строка(Номенклатура.УникальныйИдентификатор()) + "_" + Строка(Склад.УникальныйИдентификатор()) и один раз заполнить его данными из запроса. Тогда во вложенном цикле получение значения будет происходить мгновенно.

Также важно помнить об условии «не выводить пустые колонки». В нашем алгоритме это решается автоматически на Шаге 2: мы строим массив складов только на основании тех записей, которые вернул запрос остатков. Если по складу нет ни одного остатка, он просто не попадет в выборку и, соответственно, не будет отрисован в шапке.

Итоги

Мы проанализировали процесс создания сложного кросс-отчета без использования автоматизированных систем 1С. Такой подход требует от разработчика четкого понимания работы с объектом ТабличныйДокумент, особенно в части методов Вывести() и Присоединить(). Мы выяснили, что ключ к успеху кроется в правильной подготовке данных (сворачивание уникальных колонок) и эффективном поиске значений на пересечении строк и столбцов. Несмотря на то, что СКД позволяет сделать это быстрее, умение писать такие отчеты вручную является признаком высокого профессионализма и глубокого знания платформы.

← На главную