Почему в отчете 1С дублируются строки с контрагентами и как это исправить?

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

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

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

Анализ причины возникновения дублей

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

Рассмотрим пример кода, который приводит к ошибке:


Для Каждого ттс из ИНН_ Цикл
    // Если у одного контрагента два договора (в рублях и евро), 
    // этот цикл выполнится дважды для одного и того же контрагента.
    Секция2.Параметры.Контрагент = ттс.Контрагент;
    ТабличныйДокумент.Вывести(Секция2);
КонецЦикла;

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

Решение 1: Использование метода Свернуть()

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

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

Разберем алгоритм подготовки таблицы:

  1. Приведем все суммы к одной валюте (например, пересчитаем евро в рубли).
  2. Удалим или проигнорируем колонку Договор.
  3. Выполним метод Свернуть.
  4. Запустим цикл вывода уже по чистой таблице.

Посмотрим на пример реализации:


// Шаг 1: Обработка валют в цикле
Для Каждого Стр Из ИНН_ Цикл
    Если Стр.ДоговорКонтрагентаВалютаВзаиморасчетов.Код = "978" Тогда // Евро
        // Пересчитываем Сумму1 (валюта) в Сумму2 (рубли)
        Стр.Сумма2 = Стр.Сумма1 * КурсЕвро.Курс;
        Стр.Сумма1 = 0; // Обнуляем валютную колонку, если она больше не нужна
    Иначе
        // Для рублей Сумма2 уже является итоговой
        Стр.Сумма2 = Стр.Сумма1;
        Стр.Сумма1 = 0;
    КонецЕсли;
КонецЦикла;

// Шаг 2: Группировка данных
// Первым параметром передаем колонки, по которым ГРУППИРУЕМ
// Вторым параметром — колонки, которые СУММИРУЕМ
ИНН_.Свернуть("Контрагент, ИНН", "Сумма1, Сумма2");

// Шаг 3: Теперь выводим в отчет чистые данные без дублей
Для Каждого ттс из ИНН_ Цикл
    Секция2.Параметры.Контрагент = ттс.Контрагент;
    Секция2.Параметры.Сумма1 = ттс.Сумма1;
    Секция2.Параметры.Сумма2 = ттс.Сумма2;
    ТабличныйДокумент.Вывести(Секция2);
КонецЦикла;

Решение 2: Группировка на уровне запроса

Рассмотрим более профессиональный подход. Если данные в таблицу ИНН_ попадают из запроса, то оптимальнее всего избавиться от дублей еще на этапе получения данных из базы. Это значительно повышает производительность системы.

Проанализируем структуру запроса. Чтобы не было дублей по контрагентам, нужно исключить поле Договор из секции ВЫБРАТЬ и использовать агрегатную функцию СУММА в сочетании с СГРУППИРОВАТЬ ПО.

Пример запроса:


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

В этом случае платформа сама просуммирует все долги по всем договорам контрагента и вернет ровно одну строку на каждого уникального контрагента.

Типичные ошибки при работе с коллекциями

Разберем несколько важных технических моментов, на которых часто спотыкаются начинающие разработчики:

  1. Изменение таблицы внутри цикла: Никогда не вызывайте метод Свернуть() или Удалить() для таблицы внутри цикла Для Каждого по этой же таблице. Это приведет к непредсказуемому поведению итератора и пропуску строк. Сначала обрабатываем значения в колонках, потом (вне цикла) сворачиваем.
  2. Типы данных при свертке: Если метод Свернуть выдает ошибку "Неверное имя колонки", проверьте дважды:
    • Совпадает ли регистр букв в названии колонки.
    • Является ли колонка, указанная для суммирования, числовой (Число). Если там будет Null или Неопределено, метод может не сработать.
  3. Использование поиска строк: Часто программисты используют НайтиСтроки() внутри цикла обхода. Это замедляет отчет. Если вам нужно сопоставить данные из двух таблиц, лучше один раз проиндексировать их по ключевому полю: ТаблицаОстатков.Индексы.Добавить("Контрагент");

Итоговые рекомендации

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


// 1. Получаем данные (в запросе или из регистра)
// 2. Если есть валютные расчеты — приводим их к единому знаменателю (к рублям)
Для Каждого СтрокаТЗ Из ИсходнаяТаблица Цикл
    // Логика пересчета валют
КонецЦикла;

// 3. Убираем детализацию, которая вызывает дубли (Договоры, Сделки)
// Оставляем только Контрагента и Суммы
ИсходнаяТаблица.Свернуть("Контрагент, ИНН", "Сумма");

// 4. Выводим сгруппированный результат
Для Каждого СтрокаРезультата Из ИсходнаяТаблица Цикл
    // Заполнение параметров секции
    // Вывод в ТабличныйДокумент
КонецЦикла;

Помните: правильная подготовка данных — это 90% успеха в разработке отчетов. Группировка данных (свертка) должна происходить до того, как первая строка попадет в печатную форму. Это гарантирует отсутствие дублей и корректность итоговых сумм в вашем отчете.

← На главную