В практике разработки на платформе 1С:Предприятие часто возникает задача обработки данных, которые хранятся в «горизонтальном» виде. Типичный пример — документ «План продаж», где колонки соответствуют дням месяца, периодам или определенным показателям. Для построения графиков, формирования отчетов или интеграции с другими системами нам необходимо «развернуть» эти данные, пройдясь по каждой заполненной колонке в строке.
В этой статье мы подробно разберем, как эффективно решить эту задачу, начиная от простого перебора строк и заканчивая профессиональными методами работы с метаданными и запросами.
Рассмотрим самый прямой и понятный способ решения. Если у нас уже есть таблица значений (ТЗ), полученная из табличной части документа, мы можем запустить цикл по строкам, а внутри него — цикл по всем доступным колонкам этой таблицы. Это позволит нам проверить каждую ячейку на наличие данных.
Разберем пример кода, который позволяет вывести информацию о заполненных колонках в окно сообщений:
// Сначала выгружаем табличную часть документа в таблицу значений
ТаблЗначений = Объект.ПланПродаж.Выгрузить();
// Начинаем обход строк таблицы
Для Каждого СтрокаТЗ Из ТаблЗначений Цикл
// Для каждой строки запускаем внутренний цикл по метаданным колонок ТЗ
Для Каждого Колонка Из ТаблЗначений.Колонки Цикл
// Получаем текущее значение ячейки по имени колонки
ТекущееЗначение = СтрокаТЗ[Колонка.Имя];
// Проверяем, заполнено ли значение (для чисел 0 может считаться пустым)
Если ЗначениеЗаполнено(ТекущееЗначение) Тогда
Сообщить("В строке № " + СтрокаТЗ.НомерСтроки +
" колонка '" + Колонка.Имя +
"' содержит значение: " + ТекущееЗначение);
КонецЕсли;
КонецЦикла;
КонецЦикла;
Важный нюанс: при работе с числовыми данными (планами) функция ЗначениеЗаполнено() возвращает Ложь для значения 0. Если в вашей логике нулевой план — это тоже важная информация, лучше использовать прямое сравнение ТекущееЗначение <> 0 или проверку на тип данных.
Проанализируем ситуацию, когда нам нужно обрабатывать не все колонки подряд, а только те, которые отвечают за «дни» (например, «День1», «День2» и т.д.). Хардкодить имена колонок — плохая практика. Вместо этого мы можем использовать объектную модель метаданных.
Давайте выясним, как программно отобрать нужные реквизиты табличной части:
// Получаем доступ к метаданным табличной части документа
МетаданныеТЧ = Метаданные.Документы.ПланПродаж.ТабличныеЧасти.СоставПлана;
Для Каждого СтрокаДокумента Из Объект.СоставПлана Цикл
// Перебираем только те реквизиты, которые описаны в конфигураторе для этой ТЧ
Для Каждого РеквизитТЧ Из МетаданныеТЧ.Реквизиты Цикл
// Проверяем имя реквизита на соответствие шаблону (например, начинается на "День")
Если Найти(РеквизитТЧ.Имя, "День") > 0 Тогда
ЗначениеДня = СтрокаДокумента[РеквизитТЧ.Имя];
Если ЗначениеДня > 0 Тогда
// Здесь логика добавления точки на график
ОбработатьДанныеДляГрафика(СтрокаДокумента.Сотрудник, РеквизитТЧ.Имя, ЗначениеДня);
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецЦикла;
Этот метод гораздо надежнее, так как он автоматически адаптируется под изменения в структуре документа (например, если вы добавите 31-й день в таблицу).
Если ваша конечная цель — построение графика или диаграммы, то наиболее эффективным методом будет перевод «горизонтальных» данных в «вертикальные» еще на этапе получения данных из базы. В языке запросов 1С нет оператора UNPIVOT, но мы можем реализовать его через ОБЪЕДИНИТЬ ВСЕ.
Посмотрим на пример структуры такого запроса. Это позволит избежать сложных циклов в коде 1С:Предприятие и сразу получить готовую таблицу для серии графика:
ВЫБРАТЬ
ПланПродаж.Сотрудник КАК Сотрудник,
"День 1" КАК ПериодДень,
ПланПродаж.День1 КАК Значение
ИЗ
Документ.ПланПродаж.СоставПлана КАК ПланПродаж
ГДЕ
ПланПродаж.Ссылка = &Ссылка И ПланПродаж.День1 > 0
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
ПланПродаж.Сотрудник,
"День 2",
ПланПродаж.День2
ИЗ
Документ.ПланПродаж.СоставПлана КАК ПланПродаж
ГДЕ
ПланПродаж.Ссылка = &Ссылка И ПланПродаж.День2 > 0
Преимущества этого подхода:
Диаграмма или использовать как источник данных для системы компоновки данных (СКД).ГДЕ, не нагружая память сервера лишними данными.Если данные уже находятся в памяти в виде ТаблицаЗначений, и их объем велик, мы можем применить методы быстрой фильтрации. Рассмотрим использование метода НайтиСтроки() для предварительного отбора данных по сотруднику, чтобы не перебирать всю таблицу целиком.
// Создаем структуру отбора
ПараметрыОтбора = Новый Структура;
ПараметрыОтбора.Вставить("Сотрудник", ВыбранныйСотрудник);
// Получаем массив только нужных строк
НайденныеСтроки = ТаблЗначений.НайтиСтроки(ПараметрыОтбора);
Для Каждого Строка Из НайденныеСтроки Цикл
// Обход колонок выполняется только для целевого сотрудника
Для Каждого Колонка Из ТаблЗначений.Колонки Цикл
// Логика обработки...
КонецЦикла;
КонецЦикла;
При построении графиков важно помнить о терминологии 1С. Точка диаграммы — это обычно ваше измерение времени (в данном случае — день, который мы вытянули из названия колонки). Серия — это разрез данных (например, «План» или «Факт»).
Проанализируем процесс заполнения программно созданной диаграммы:
Диаграмма.Очистить().Диаграмма.УстановитьЗначение(Точка, Серия, Значение) для отрисовки графика.Таким образом, комбинируя обход коллекции Колонки и проверку ЗначениеЗаполнено, вы сможете легко трансформировать плоскую таблицу документа в динамический и наглядный график для пользователя.