Разработка отчетов в системе 1С:Предприятие 7.7 требует внимательного подхода к структуре запросов и алгоритмам обработки таблиц значений. Одной из часто встречающихся задач является построение «шахматки», где по строкам выводится номенклатура, а по столбцам — продажи за произвольные периоды. В процессе реализации программисты часто сталкиваются с дублированием строк, некорректными кодами позиций и потерей данных в последней строке отчета. Разберем, как правильно построить такой отчет и исправить типичные ошибки.
Рассмотрим ситуацию, когда при использовании группировки Товар упорядочить по Товар.Наименование без групп все начинают выводиться лишние строки или неверные коды. Выясним причину этого поведения. В 1С 7.7 группировка по реквизиту объекта (например, по коду или наименованию внутри запроса по документам) может привести к «размножению» строк, если один и тот же товар встречается в разных контекстах. Правильный подход — группировать данные только по объекту (ссылке), а дополнительные данные (код, артикул) получать непосредственно при выводе.
Проанализируем фрагмент текста запроса, который вызывает проблемы:
|Группировка Товар упорядочить по Товар.Наименование без групп все;
|Группировка Месяц все;
|Группировка Код; // Ошибка: лишняя группировка
Важный момент: Группировка по Код здесь избыточна, так как код является атрибутом товара. Если товар уже сгруппирован, повторная группировка по его коду ломает иерархию и может привести к выводу пустых значений. Вместо этого в коде вывода следует обращаться к Запрос.Товар.Код.
Для вывода продаж по разным периодам (например, несколько месяцев подряд) можно использовать два подхода: выполнение запроса в цикле по периодам или один большой запрос с группировкой по времени. Рассмотрим первый вариант, который чаще выбирают начинающие разработчики, и разберем его оптимизацию.
При выполнении запроса в цикле мы собираем данные во вспомогательную таблицу значений (ТЗ). Чтобы в отчет попали товары, по которым не было движений (нулевые продажи), необходимо использовать флаг все в группировке по товару. Однако помните, что флаг все работает эффективно только при наличии условий на список элементов или если товар присутствовал в оборотах за другие периоды.
Для й = 1 По КолПериодов Цикл
НачДата = ТЗПериод.ПолучитьЗначение(й, "Период_С");
КонДата = ТЗПериод.ПолучитьЗначение(й, "Период_По");
Запрос = СоздатьОбъект("Запрос");
ТекстЗапроса = "//{{ЗАПРОС(Сформировать)
|Период с НачДата по КонДата;
|Товар = Документ.ОтчетККМ.Номенклатура.ТекущийЭлемент;
|Количество = Документ.ОтчетККМ.Количество;
|Функция КоличествоСумма = Сумма(Количество);
|Группировка Товар упорядочить по Товар.Наименование без групп все;
|Условие(Товар в СписокЭлементовМФ);
|"//}}ЗАПРОС;
Если Запрос.Выполнить(ТекстЗапроса) = 1 Тогда
// Выгружаем и добавляем в общую ТЗВспом с пометкой периода
КонецЕсли;
КонецЦикла;
Одной из самых коварных ошибок является алгоритм вывода данных при смене номенклатуры. Посмотрим на классический цикл обхода таблицы значений. В нем секция итогов по товару обычно выводится в момент, когда Номенклатура <> ПредыдущаяНоменклатура. Это приводит к тому, что когда цикл доходит до последней строки таблицы, условие смены номенклатуры больше не срабатывает, и данные по последней позиции не попадают в печатную форму или не закрываются итогом.
Чтобы этого избежать, проанализируем правильную структуру вывода:
Пример корректного алгоритма вывода:
ТЗВспом.Сортировать("Товар, Период +");
Номен = "";
Для N = 1 По ТЗВспом.КоличествоСтрок() Цикл
ТЗВспом.ПолучитьСтрокуПоНомеру(N);
ТекущийТовар = ТЗВспом.Товар;
Если Номен <> ТекущийТовар Тогда
Если N > 1 Тогда
// Выводим итоги по предыдущему товару (Период, Прирост и т.д.)
Таб.ПрисоединитьСекцию("Сформировать|ИтогиПоСтроке");
КонецЕсли;
// Выводим начало строки для нового товара
Код = ТекущийТовар.Код;
Таб.ВывестиСекцию("Сформировать|Товар");
ИтогПоСтроке = 0;
Номен = ТекущийТовар;
КонецЕсли;
// Выводим данные за конкретный период (столбец)
Кол = ТЗВспом.Количество;
ИтогПоСтроке = ИтогПоСтроке + Кол;
Таб.ПрисоединитьСекцию("Сформировать|СтолбецПериода");
КонецЦикла;
// ВАЖНО: Довывод итогов для самого последнего товара
Таб.ПрисоединитьСекцию("Сформировать|ИтогиПоСтроке");
Если в отчет попало много товаров, у которых продажи за все выбранные периоды равны нулю, их стоит отсечь перед выводом. Для этого удобно использовать метод Свернуть или предварительный цикл по таблице значений. Рассмотрим способ с предварительной очисткой:
Проанализируем таблицу перед печатью. Мы можем создать вспомогательную колонку ОбщийИтог, свернуть таблицу по товару, просуммировав продажи, и удалить те строки, где ОбщийИтог <= 0. После этого выводим только те позиции, которые остались в списке. Еще один вариант — проверять сумму итогов непосредственно в цикле вывода и использовать метод УдалитьСтроку(), двигаясь по таблице с конца в начало, чтобы не нарушить индексацию строк.
Выясним, как ускорить формирование отчета. Выполнение запроса внутри цикла (особенно если периодов много) — это серьезная нагрузка на базу данных. Профессиональный подход заключается в получении всех данных одним запросом за весь интервал (от самой первой даты до самой последней). В таком запросе используется группировка Месяц или Период. После выполнения запроса данные выгружаются в ТЗ, которая затем разворачивается в горизонтальный отчет программными средствами. Это сокращает количество обращений к SQL-серверу или файловой базе до минимума.