При разработке сложных аналитических инструментов на базе 1С часто возникает задача: не просто вывести отчет с помощью Системы компоновки данных (СКД), но и позволить пользователю отредактировать полученные цифры или текстовые комментарии прямо в форме отчета, а затем сохранить эти изменения или использовать их в дальнейших расчетах. В этой статье мы подробно разберем, как «дотянуться» до данных, которые пользователь внес в ТабличныйДокумент, и как правильно сопоставить эти строки с реальными объектами базы данных.
Когда СКД выводит результат, она формирует ТабличныйДокумент. На этом этапе живая связь с источником данных (запросом или объектной схемой) обрывается. Перед программистом встает вопрос: как понять, что число в строке №10 относится именно к «Номенклатуре: Смартфон», если пользователь мог изменить и само название, и значение в колонке? Рассмотрим наиболее эффективные способы решения этой задачи.
Проанализируем самый надежный метод идентификации данных. Каждая ячейка, сформированная СКД, содержит в себе индекс расшифровки. Этот индекс позволяет нам получить доступ к структуре полей, которые послужили основой для вывода этой конкретной строки.
Для реализации этого метода нам потребуется объект ДанныеРасшифровкиКомпоновкиДанных. Разберем по шагам, как это работает в коде:
// 1. Получаем объект расшифровки из временного хранилища (обычно хранится в реквизите формы ДанныеРасшифровки)
Данные = ПолучитьИзВременногоХранилища(ДанныеРасшифровки);
// 2. Определяем область, которую хотим обработать (например, весь табличный документ)
ТабДок = Отчет.Результат;
Для НомерСтроки = 1 По ТабДок.ВысотаТаблицы Цикл
Ячейка = ТабДок.Область(НомерСтроки, 1); // Допустим, идентификатор в первой колонке
// 3. Получаем идентификатор расшифровки из ячейки
ИдРасшифровки = Ячейка.Расшифровка;
Если ИдРасшифровки <> Неопределено Тогда
// 4. Извлекаем содержимое расшифровки
Содержимое = Данные.Элементы.Получить(ИдРасшифровки);
Если ТипЗнч(Содержимое) = Тип("ДанныеРасшифровкиПоля") Тогда
Для Каждого Поле Из Содержимое.ПолучитьПоля() Цикл
Если Поле.Поле = "Ссылка" Тогда
ОбъектБД = Поле.Значение; // Вот мы и нашли наш объект!
// Теперь берем измененный текст из нужной колонки
НовоеЗначение = ТабДок.Область(НомерСтроки, 3).Текст;
// Обрабатываем...
КонецЕсли;
КонецЦикла;
КонецЕсли;
КонецЕсли;
КонецЦикла;
Важный момент: использование расшифровки гарантирует, что даже если пользователь удалит часть текста в ячейке, скрытый идентификатор останется (если не была очищена вся ячейка целиком).
Выясним причину, по которой обычный перебор ТабДок.Область().Текст может привести к ошибкам. Если пользователь вводит число или дату, то в свойстве Текст мы получаем строковое представление. Это неудобно для вычислений. Мы можем настроить ячейки так, чтобы они хранили типизированное значение.
Рассмотрим алгоритм настройки:
ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент) необходимо перехватывать момент вывода каждой области.СодержатьЗначение = Истина.ТипЗначения (например, ОписаниеТипов("Число")).В этом случае при обработке результата мы будем обращаться не к .Текст, а к свойству .Значение:
ТекущаяЯчейка = ТабДок.Область(Строка, Колонка);
Если ТекущаяЯчейка.СодержатьЗначение Тогда
Сумма = ТекущаяЯчейка.Значение; // Получаем чистое число, а не строку
КонецЕсли;
Если работа с объектом ДанныеРасшифровки кажется избыточной, проанализируем более простой «хакерский» метод. Мы можем добавить в настройки СКД поле, содержащее уникальный идентификатор строки (например, GUID или Код), и вывести его в отдельную колонку.
Разберем, как скрыть эти данные от пользователя:
// Скрываем колонку с идентификатором (например, колонку №2)
ТабДок.Область(, 2, , 2).ШиринаКолонки = 0;
// При обработке:
Идентификатор = ТабДок.Область(НомерСтроки, 2).Текст;
Чтобы не перебирать тысячи строк в поисках одной измененной ячейки, воспользуемся событиями элемента формы Результат (тип ПолеТабличногоДокумента). Посмотрим на пример использования события ПриИзмененииСодержимогоОбласти.
Это событие срабатывает, когда пользователь завершает редактирование ячейки. Мы можем сразу фиксировать изменения в специальную вспомогательную Таблицу Значений на форме:
&НаКлиенте
Процедура РезультатПриИзмененииСодержимогоОбласти(Элемент, Область)
// Область — это именно та ячейка, которую поменяли
НомерСтроки = Область.Верх;
НовоеЗначение = Область.Текст;
// Можем сразу найти ID строки через расшифровку или скрытую колонку
// и записать в ТЗ "ИзмененныеДанные"
КонецПроцедуры
Если структура вашего отчета плоская (простой список без группировок), иногда проще всего полностью «выгрузить» содержимое ТабличногоДокумента в ТаблицуЗначений и работать уже с ней как с обычным объектом 1С.
Посмотрим на пример универсальной функции обхода:
Функция ТЗИзТабличногоДокумента(ТабДок) Экспорт
ТЗ = Новый ТаблицаЗначений;
// Создаем колонки на основании заголовков (допустим, строка 1)
Для Кол = 1 По ТабДок.ШиринаТаблицы Цикл
ТЗ.Колонки.Добавить("Колонка" + Кол);
КонецЦикла;
// Заполняем данными
Для Стр = 2 По ТабДок.ВысотаТаблицы Цикл
НоваяСтрока = ТЗ.Добавить();
Для Кол = 1 По ТабДок.ШиринаТаблицы Цикл
НоваяСтрока["Колонка" + Кол] = ТабДок.Область(Стр, Кол).Текст;
КонецЦикла;
КонецЦикла;
Возврат ТЗ;
КонецФункции
При работе с большими отчетами (свыше 5000 строк) прямой перебор ячеек на клиенте может привести к значительному замедлению интерфейса («фризам»). В таких случаях рекомендуется передавать табличный документ на сервер и выполнять его разбор там. На сервере обращение к областям ячеек происходит быстрее, а использование расшифровки через ПолучитьИзВременногоХранилища работает стабильнее.
Подведем итог: для простого чтения данных достаточно цикла по ВысотаТаблицы. Для профессиональной разработки и надежной связи с базой данных следует использовать ДанныеРасшифровки. Если же отчет предполагает массовый ввод данных пользователем, лучшим выбором будет использование Таблицы Значений в качестве источника данных для формы, вместо прямого редактирования результата СКД.