В практике разработки на платформе 1С часто возникает задача формирования отчетов, где данные должны располагаться в виде сетки или расписания: по одной оси (например, горизонтальной) идут даты, а по другой — объекты (заказы, контрагенты или ресурсы). Реализация такой «шахматки» требует особого подхода к построению циклов и работе с областями макета. В этой статье мы подробно разберем, как правильно организовать вывод данных, оптимизировать запрос и избежать типичных ошибок начинающих разработчиков.
Рассмотрим ситуацию, когда при попытке вывода расписания в табличный документ попадает только одна запись или даты отображаются неверно. Основная причина кроется в неправильной вложенности циклов и некорректном управлении переменной периода. Если мы обходим выборку запроса, а внутри нее запускаем цикл по датам, не сбрасывая начальную дату периода, алгоритм отработает корректно только для первой строки. Все последующие записи не пройдут условие по дате, так как счетчик периода уже достигнет конечного значения.
Кроме того, часто используются избыточные ЛЕВОЕ СОЕДИНЕНИЕ или группировки в запросах, что замедляет работу системы и усложняет чтение кода. Разберем по шагам, как привести алгоритм в порядок.
В 1С:Предприятии 8 доступен механизм обращения к реквизитам через «точку». Это означает, что если у нас есть ссылка на документ, нам не нужно делать соединение с таблицей этого документа, чтобы получить его дату или контрагента. Платформа сделает это автоматически (неявное соединение).
Проанализируем пример оптимизированного запроса. Вместо сложных соединений табличной части и шапки документа, мы будем обращаться к данным напрямую:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ЗаказыТЧ.Заказ КАК Заказ,
| ЗаказыТЧ.Вес КАК Вес,
| ЗаказыТЧ.Объем КАК Объем,
| ЗаказыТЧ.Сумма КАК Сумма,
| ЗаказыТЧ.Ссылка.ДеньОтгрузки КАК ДатаОтгрузки,
| ЗаказыТЧ.Ссылка.Контрагент КАК Контрагент
|ИЗ
| Документ.ПС_ЗаказыДляОтгрузки.Заказы КАК ЗаказыТЧ
|ГДЕ
| НЕ ЗаказыТЧ.Ссылка.ПометкаУдаления
| И ЗаказыТЧ.Ссылка.ДеньОтгрузки МЕЖДУ &НачалоПериода И &КонецПериода
|УПОРЯДОЧИТЬ ПО
| ДатаОтгрузки";
Запрос.УстановитьПараметр("НачалоПериода", _Период);
Запрос.УстановитьПараметр("КонецПериода", _КонПериод);
РезультатЗапроса = Запрос.Выполнить();
Обратите внимание на использование УПОРЯДОЧИТЬ ПО. Для формирования расписания критически важно, чтобы данные из базы приходили в строгой последовательности по времени.
Для создания шахматки нам понадобятся именованные области макета. Обычно это:
Важно понимать различие между методами Вывести() и Присоединить(). Метод Вывести() всегда начинает новую строку в табличном документе. Метод Присоединить() добавляет область справа от уже выведенных данных в той же строке.
Чтобы получить полноценное расписание, мы должны сделать основным циклом обход дат, а не обход выборки запроса. Однако, наиболее производительным способом будет предварительная выгрузка данных запроса в ТаблицуЗначений. Это позволит нам быстро находить нужные заказы на конкретную дату без повторного обращения к БД.
Рассмотрим правильный алгоритм вывода:
// Выгружаем данные для удобного поиска
ТаблицаДанных = РезультатЗапроса.Выгрузить();
ТаблицаДанных.Индексы.Добавить("ДатаОтгрузки");
ТекущаяДата = _Период;
Пока ТекущаяДата <= _КонПериод Цикл
// Получаем область колонки для текущей даты
ОбластьДень = МакетПечать.ПолучитьОбласть("Таблица|Дата");
ОбластьДень.Параметры.Дата = Формат(ТекущаяДата, "ДФ='dd.MM.yyyy (ddd)'");
// Выводим или присоединяем колонку
Если ТекущаяДата = _Период Тогда
ТабДок.Вывести(ОбластьДень);
Иначе
ТабДок.Присоединить(ОбластьДень);
КонецЕсли;
// Ищем данные на эту дату
ПараметрыОтбора = Новый Структура("ДатаОтгрузки", ТекущаяДата);
НайденныеСтроки = ТаблицаДанных.НайтиСтроки(ПараметрыОтбора);
Для Каждого СтрокаДанных Из НайденныеСтроки Цикл
// Здесь логика вывода конкретных заказов под датой
// Нужно учитывать, что вывод данных вниз внутри колонки даты
// требует сложной координатной работы или использования вложенных таблиц
КонецЦикла;
ТекущаяДата = ТекущаяДата + 86400; // Переход к следующему дню (сутки в секундах)
КонецЦикла;
Если мы просто будем использовать Присоединить(), сетка расписания может «поехать», если на одну дату заказов много, а на другую — нет. Чтобы этого избежать, профессиональные разработчики используют один из двух приемов:
Строка + Дата есть данные — выводим их, если нет — выводим пустую ячейку макета. Это гарантирует сохранение структуры таблицы.ПолучитьОбласть("Строка|Колонка") позволяет получить конкретную ячейку на их пересечении.Если задача позволяет, лучше отказаться от ручного вывода в пользу СКД. Это наиболее современный и эффективный способ создания шахматок. В настройках отчета достаточно выбрать тип «Таблица». В «Строки» добавить группировку по заказам/контрагентам, а в «Колонки» — группировку по периоду (день). Платформа сама позаботится о:
Для реализации дополнения периодов в СКД нужно в настройках поля группировки («Дата») установить тип дополнения «День» и указать начальную и конечную даты. Это автоматически создаст колонки для всех дней месяца, независимо от наличия заказов в базе.
Проанализируем частую ошибку сравнения: Если ТекущаяДата = Выборка.ДатаОтгрузки. В 1С тип Дата содержит время. Если в базе заказ записан на 12.10.2023 14:30:00, а в цикле вы проверяете 12.10.2023 00:00:00, условие не выполнится. Всегда используйте функцию НачалоДня() как в запросе, так и в коде 1С, чтобы обнулить время и сравнивать только календарные сутки.
Выполняя эти рекомендации, мы получим надежный и быстрый механизм вывода расписания, который легко масштабируется под любые задачи управленческого учета.