Свёртка таблицы без потери данных

05.07.13

Разработка - Универсальные функции

Было в таблице значений 3 колонки, по одной группировали, по другой суммировали, а третья и потерялась... Знакомая сказка? Чтобы не плодить вспомогательные данные, эта функция сворачивает таблицу так, что в специальной колонке будут подтаблицы, хранящие "свёрнутые" куски в первозданном виде. Может быть интересно изучающим некоторые фичи СКД.

Собственно, всё упихнуто в одну функцию, и стандартная свёртка тоже. Проверено на практике, но на тестовом массиве с небольшим разнообразием. Я вот пока не очень уверен, что в любом случае будет работать - например, при каких-нибудь экзотических типах колонок или их наполнении. Но если кому пригодится, и хорошо. Как пример работы с СКД, кстати ))) 

// рИсточник - собственно объект-источник (обычно таблица значений, её имя определяется автоматически)
Функция ПоместитьРезультатСКДвТаблицуЗначений(рСКД,рКомпНастроекИлиНастройки,рИсточник="") Экспорт
Попытка
   
РезультатнаяТаблица=Новый ТаблицаЗначений;
   
//
    // исходя из типа источника
   
рНабор=рСКД.НаборыДанных.Получить(0); // основной набор
    //
   
КомпоновщикМакета=Новый КомпоновщикМакетаКомпоновкиДанных;
   
рТипГенератора=Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений");
    Если
ТипЗнч(рКомпНастроекИлиНастройки)=Тип("НастройкиКомпоновкиДанных") Тогда
       
МакетКомпоновки=КомпоновщикМакета.Выполнить(рСКД,рКомпНастроекИлиНастройки,,,рТипГенератора);
    ИначеЕсли
ТипЗнч(рКомпНастроекИлиНастройки)=Тип("КомпоновщикНастроекКомпоновкиДанных") Тогда
       
МакетКомпоновки=КомпоновщикМакета.Выполнить(рСКД,рКомпНастроекИлиНастройки.Настройки,,,рТипГенератора);
    КонецЕсли;
   
ПроцессорКД=Новый ПроцессорКомпоновкиДанных;
    Если
ТипЗнч(рНабор)=Тип("НаборДанныхОбъектСхемыКомпоновкиДанных") Тогда
       
ПроцессорКД.Инициализировать(МакетКомпоновки,Новый Структура(СокрЛП(рНабор.ИмяОбъекта),рИсточник));
    Иначе
       
ПроцессорКД.Инициализировать(МакетКомпоновки);
    КонецЕсли;
   
ПроцессорВывода=Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
   
ПроцессорВывода.УстановитьОбъект(РезультатнаяТаблица);
   
#Если Клиент Тогда
       
ПроцессорВывода.ОтображатьПроцентВывода=Истина;
   
#КонецЕсли
   
РезультатнаяТаблица=ПроцессорВывода.Вывести(ПроцессорКД,Истина);
    Возврат
РезультатнаяТаблица;
Исключение
   
Сообщить("ПоместитьРезультатСКДвТаблицуЗначений: ошибка: "+ОписаниеОшибки(),СтатусСообщения.ОченьВажное);
    Возврат
РезультатнаяТаблица;
КонецПопытки;
КонецФункции

// Свёртка таблицы, возвращаемое значение типа "ТаблицаЗначений"
// Параметры:
//      рТаблица - исходная таблица значений; допустимы любые типы колонок, доступные к выборке в СКД;
//      рПоляСвертки - имена колонок таблицы через запятую; по их одинаковым значениям будет выполняться группировка;
//           если строка пуста, и не пуста строка рПоляСуммы, то все колонки, не входящие в суммируемые, войдут в группируемые.
//      рПоляСумма - имена колонок таблицы через запятую, значения этих колонок суммируются по общим принципам свёртки
//           и суммирования полей в СКД; если строка пуста, то: а) при пустой строке свёртки будет предпринята попытка заполнить
//           строку именами колонок, имеющих числовой тип (хотя бы и среди других типов), и если не удастся, завершит функцию;
//           б) при непустой строке свёртки и непустом имени колонки-агрегатора выполнит свёртку, не суммируя ни по какому полю;
//           в) при непустой строке свёртки и пустом имени колонки-агрегатора выполнит обычную свёртку без суммовых колонок;
//      рИмяКолонкиАгрегатора - необязательное имя поля колонки для вложенных таблиц значений; если не указано, происходит
//           обычная свёртка средствами 1С, а если указано, в результатную таблицу вносится колонка с этим именем, содержащая
//           таблицы значений "фрагментов" (блоков), соответствующих правилам свёртки; причём колонки, участвующие в суммиро-
//           вании (т.е. из рПоляСуммы) также в исходном, непросуммированном виде присутствуют в этих вложеных подтаблицах.
//
Функция СвернутьТаблицу(Знач рТаблица,Знач рПоляСвертки,Знач рПоляСуммы,рИмяКолонкиАгрегатора="") Экспорт
Попытка
   
мПоляСвертки=ОбщегоНазначения.РазложитьСтрокуВМассивПодстрок(рПоляСвертки,",");
   
мПоляСуммы=ОбщегоНазначения.РазложитьСтрокуВМассивПодстрок(рПоляСуммы,",");
    Если
ТипЗнч(рТаблица)<>Тип("ТаблицаЗначений") Тогда Возврат рТаблица КонецЕсли;
    Если
рТаблица.Колонки.Количество()=0 Тогда Возврат рТаблица КонецЕсли;

    Если
мПоляСвертки.Количество()=0 и мПоляСуммы.Количество()=0 Тогда
       
// определяем по типам значений
       
рПоляСуммы=""; разд="";
        Для каждого
кол Из рТаблица.Колонки Цикл
            Если
кол.ТипЗначения.СодержитТип(Тип("Число")) Тогда
               
мПоляСуммы.Добавить(СокрЛП(кол.Имя));
               
рПоляСуммы=рПоляСуммы+разд+СокрЛП(кол.Имя); разд=",";
            КонецЕсли;
        КонецЦикла;
        Если
мПоляСуммы.Количество()=0 Тогда // ничего поделать нельзя
           
Возврат рТаблица;
        КонецЕсли;
    КонецЕсли;

    Если
мПоляСвертки.Количество()=0 и мПоляСуммы.Количество()<>0 Тогда
       
// все поля, не входящие в суммируемые, подразумеваются как свёрточные
       
рПоляСвертки=""; разд="";
        Для каждого
кол Из рТаблица.Колонки Цикл
            Если
мПоляСуммы.Найти(СокрЛП(кол.Имя))=Неопределено Тогда
               
мПоляСвертки.Добавить(СокрЛП(кол.Имя));
               
рПоляСвертки=рПоляСвертки+разд+СокрЛП(кол.Имя); разд=",";
            КонецЕсли;
        КонецЦикла;
    КонецЕсли;

    Если
ПустаяСтрока(рИмяКолонкиАгрегатора) Тогда // режим агрегирования не используется
       
рТаблица.Свернуть(рПоляСвертки,рПоляСуммы);
    КонецЕсли;

    Если
ПустаяСтрока(рПоляСвертки)
    или
мПоляСвертки.Количество()=0
   
или ПустаяСтрока(рИмяКолонкиАгрегатора)
    Тогда Возврат
рТаблица КонецЕсли;

   
рСКД=Новый СхемаКомпоновкиДанных;
   
//
   
рИсточникДанных=рСКД.ИсточникиДанных.Добавить();
   
рИсточникДанных.Имя="ОсновнойИсточник";
   
рИсточникДанных.ТипИсточникаДанных="Local";
   
//
   
рНабор=рСКД.НаборыДанных.Добавить(Тип("НаборДанныхОбъектСхемыКомпоновкиДанных"));
   
рНабор.Имя="ОсновнойНабор";
   
рНабор.ИмяОбъекта="ТаблицаИсточник"; // связывание с внешними данными идёт именно по нему
   
рНабор.ИсточникДанных="ОсновнойИсточник";
   
//
   
рНастройка=рСКД.НастройкиПоУмолчанию;
   
рГруппировкаКД=рНастройка.Структура.Добавить(Тип("ГруппировкаКомпоновкиДанных"));
    Для каждого
рИмяПоляСвертки Из мПоляСвертки Цикл
       
рПолеГр=рГруппировкаКД.ПоляГруппировки.Элементы.Добавить(Тип("ПолеГруппировкиКомпоновкиДанных"));
       
рПолеГр.Поле=Новый ПолеКомпоновкиДанных(СокрЛП(рИмяПоляСвертки));
       
рПолеГр.ТипГруппировки=ТипГруппировкиКомпоновкиДанных.Элементы;
    КонецЦикла;
   
рГруппировкаКД.Выбор.Элементы.Добавить(Тип("АвтоВыбранноеПолеКомпоновкиДанных"));
   
//
   
мОстающихся=Новый Массив;
   
стрОстающихся=""; разд="";
    Для каждого
кол Из рТаблица.Колонки Цикл
       
#Если Клиент Тогда
           
ОбработкаПрерыванияПользователя();
       
#КонецЕсли
       
// вносим поля в набор, это обязательно
       
рПоле=рНабор.Поля.Добавить(Тип("ПолеНабораДанныхСхемыКомпоновкиДанных"));
       
рПоле.Заголовок=кол.Заголовок;
       
рПоле.Поле=кол.Имя;
       
рПоле.ПутьКДанным=кол.Имя;
       
рПоле.ТипЗначения=СКДиПостроители.ПолучитьОписаниеТипаБезПустых(кол.ТипЗначения);
       
// выбранные поля добавляем на уровень самой настройки, т.е. группы "Отчёт"
       
рВыбПоле=рНастройка.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
       
рВыбПоле.Заголовок=рПоле.Заголовок;
       
рВыбПоле.Поле=Новый ПолеКомпоновкиДанных(рПоле.ПутьКДанным);
       
// выясним, куда попадает эта колонка
       
Если мПоляСвертки.Найти(СокрЛП(кол.Имя))=Неопределено Тогда // она может быть и сумматорной
           
мОстающихся.Добавить(кол.Имя);
           
стрОстающихся=стрОстающихся+разд+кол.Имя; разд=",";
        КонецЕсли;
    КонецЦикла;
    Если
ПустаяСтрока(стрОстающихся) Тогда // вообще ничего, по сути, не надо
       
рТаблица.Свернуть(СокрЛП(рПоляСвертки));
        Возврат
рТаблица;
    КонецЕсли;

   
// вносим само поле-вычислитель
   
рВыражение="ВЫБОР КОГДА Уровень()=0 ТОГДА ВычислитьВыражение(""ТаблицаЗначений("+стрОстающихся+")"",,,""Текущая"",""Последняя"") КОНЕЦ";
   
рВычПоле=рСКД.ВычисляемыеПоля.Добавить();
   
рВычПоле.ПутьКДанным=СокрЛП(рИмяКолонкиАгрегатора);
   
рВычПоле.Заголовок="Таблицы";
   
рВычПоле.Выражение=рВыражение;
   
//
   
рИтПоле=рСКД.ПоляИтога.Добавить();
   
рИтПоле.Выражение=СокрЛП(рИмяКолонкиАгрегатора);
    Для каждого
рИмяПоляСвертки Из мПоляСвертки Цикл
       
рИтПоле.Группировки.Добавить(СокрЛП(рИмяПоляСвертки));
    КонецЦикла;
   
рИтПоле.ПутьКДанным=СокрЛП(рИмяКолонкиАгрегатора);
   
//
   
Для каждого рИмяПоляСуммы Из мПоляСуммы Цикл
       
рИтПоле=рСКД.ПоляИтога.Добавить();
       
рИтПоле.Выражение="Сумма("+СокрЛП(рИмяПоляСуммы)+")";
       
рИтПоле.ПутьКДанным=СокрЛП(рИмяПоляСуммы);
       
// а группировки оставим пустыми, чтобы шёл по всем
   
КонецЦикла;
   
//
   
рВыбПоле=рНастройка.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
   
рВыбПоле.Заголовок="Таблицы";
   
рВыбПоле.Поле=Новый ПолеКомпоновкиДанных(рВычПоле.ПутьКДанным);

   
рКомпоновщикН=Новый КомпоновщикНастроекКомпоновкиДанных;
   
рКомпоновщикН.Инициализировать(Новый ИсточникДоступныхНастроекКомпоновкиДанных(рСКД));
   
рКомпоновщикН.ЗагрузитьНастройки(рНастройка);

   
резТаблица=СКДиПостроители.ПоместитьРезультатСКДвТаблицуЗначений(рсКД,рКомпоновщикН,рТаблица);
    Если
резТаблица.Количество()<>0 Тогда
       
послстро=резТаблица[резТаблица.Количество()-1];
        Если
ТипЗнч(послстро[СокрЛП(рИмяКолонкиАгрегатора)])<>Тип("ТаблицаЗначений") Тогда
           
резТаблица.Удалить(послстро);
        КонецЕсли;
    КонецЕсли;
   
//
   
Возврат резТаблица;
Исключение
   
Сообщить("СвернутьТаблицу, ошибка: "+ОписаниеОшибки(),СтатусСообщения.ОченьВажное);
    Возврат
рТаблица;
КонецПопытки;
КонецФункции

 

Внимание! Если у кого-то не заработает или будет работать неверно - пожалуйста, сообщите, постараюсь оперативно разобраться.

Пы.Сы. Не уверен, что за такую фиговинку имеет смысл плюсить.

 

 

См. также

GUID в 1С 8.3 - как с ними быть

Универсальные функции Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Пришлось помучиться с GUID-ами немного, решил поделиться опытом, мало ли кому пригодится.

12.02.2024    4316    atdonya    22    

41

Переоткрытие внешних обработок

Универсальные функции Платформа 1С v8.3 Бесплатно (free)

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

30.11.2023    3883    ke.92@mail.ru    16    

60

Валидация JSON через XDTO (включая массивы)

WEB-интеграция Универсальные функции Механизмы платформы 1С Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

При работе с интеграциями рано или поздно придется столкнуться с получением JSON файлов. И, конечно же, жизнь заставит проверять файлы перед тем, как записывать данные в БД.

28.08.2023    8560    YA_418728146    6    

139

Печать непроведенных документов для УТ, КА, ERP. Настройка печати по пользователям, документам и печатным формам

Пакетная печать Печатные формы Адаптация типовых решений Универсальные функции Платформа 1С v8.3 1С:ERP Управление предприятием 2 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х Россия Абонемент ($m)

Расширение для программ 1С:Управление торговлей, 1С:Комплексная автоматизация, 1С:ERP, которое позволяет распечатывать печатные формы для непроведенных документов. Можно настроить, каким пользователям, какие конкретные формы документов разрешено печатать без проведения документа.

2 стартмани

22.08.2023    2020    21    progmaster    7    

3

Расширение: Быстрые отборы через буфер [Alt+C] Копировать список, [Alt+V] Вставить список, [Ctrl+C] Копировать из файлов

Инструментарий разработчика Универсальные функции Платформа 1С v8.3 Конфигурации 1cv8 1С:Розница 2 1С:ERP Управление предприятием 2 1С:Бухгалтерия 3.0 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х 1С:Зарплата и Управление Персоналом 3.x Абонемент ($m)

Копирует в буфер значения из списков, из ячеек отчетов, таблиц, настроек списков, других отборов и вставляет в выбранную настройку отбора. Работает с Объект не найден. Работает как в одной так и между разными базами 1С. Использует комбинации [Alt+C] Копировать список, [Alt+V] Вставить список. Также для копирования данных используется стандартная [Ctrl+C] (например из открытого xls, mxl, doc и т.п. файла скопировать список наименований)

1 стартмани

13.10.2022    16012    131    sapervodichka    112    

129

Система контроля ведения учета [БСП]

Универсальные функции Механизмы типовых конфигураций БСП (Библиотека стандартных подсистем) Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

В данном материале рассмотрим типовой алгоритм подсистемы контроля учета БСП в конфигурациях на примерах.

18.07.2022    7200    quazare    8    

108
Комментарии
В избранное Подписаться на ответы Сортировка: Древо развёрнутое
Свернуть все
1. AnryMc 849 01.07.13 12:51 Сейчас в теме
А зачем? Всегда можно оперировать копией ТЗ.
2. Yashazz 4707 01.07.13 13:56 Сейчас в теме
Можно. Только копия ресурс кушает, если большая, и "оперировать", т.е. искать даже по индексированной - иногда скучное дело. Я всего лишь показал альтернативу, когда "всё в одном". ))
3. EarlyBird 6 01.07.13 20:43 Сейчас в теме
жениться тебе надо
тогда будет жаль времени на всякую ненужную ботву
Yan_Malyakov; +1 Ответить
6. Yashazz 4707 02.07.13 12:04 Сейчас в теме
(3) Позвольте представить, моя жена: http://infostart.ru/community/profile/41675/ - и уже восемь лет вместе )))
(5) Смысл простой - и свернуть надо, и данные не потерять. А список или массив хороши, когда такая колонка одна. Если было 10 колонок, по 1-й и 3-й свернули, пятую суммировали, то, когда нужны все остальные, подтаблица удобнее списка.
smirnov.a; +1 Ответить
4. Гость 02.07.13 03:28
Как пример простой программной работы с СКД очень хорошо. Стоило преподносить публикацию именно как пример работы с СКД, тогда польза от нее очевидна.
5. batlerrett 02.07.13 07:51 Сейчас в теме
Смысл тогда сворачивать? Если нужны списки, так и надо в 3-й колонке значением список делать
7. EarlyBird 6 02.07.13 12:25 Сейчас в теме
честно говоря, пока для себя не вижу практического применения данного решения
если я что-то сворачиваю, я это сворачиваю. Если не надо сворачивать, я не сворачиваю.
Но всё же плюс, за конструктивные ответы (и красивую жену :) )
9. Yashazz 4707 02.07.13 13:41 Сейчас в теме
(7) Мне часто приходится сталкиваться, раньше делал через 2 таблицы (свёрнутая и исходная), но оперативку жалко. А так пыхтит сервер приложения, насколько я понял распределение нагрузки. И что-то мне подсказывает, что напрячь механизм СКД в этом случае более экономно, нежели крутить временные таблицы.

А если честно, просто захотелось побаловаться с этими функциями СКД, и всё ))
8. Const1C 02.07.13 13:01 Сейчас в теме
В семерки часто сталкиваюсь с такой задачей. Что нужно данные сохранять, после свертки. В частности при формировании группировок в отчетах. Но, ИМХО, в такой ситуации, лучше, действительно, оперировать таблицей значений. А уж если ресурсы жалко (когда, например, нужно работать с 100500 записями). Лучше использовать временные таблицы.
10. TrinitronOTV 14 04.07.13 17:18 Сейчас в теме
не смотря на комментарии, для меня всё равно полезно было ознакомиться, спасибо
11. AnryMc 849 04.07.13 21:48 Сейчас в теме
(10) TrinitronOTV, Кто к нам пожаловал...
12. Збянтэжаны Саўка 245 05.07.13 09:39 Сейчас в теме
(0) СКДиПостроители - это откуда? не встречал такого модуля.
13. sstar90 05.07.13 10:18 Сейчас в теме
(0) Присоединяюсь к (12)
Если это твой модуль, было бы логично выложить и его (в части, касающейся данного решения).
14. Yashazz 4707 05.07.13 12:00 Сейчас в теме
Ёлы-палы, сколько ж я ещё на эти грабли наступать буду... Ща, выложу нужные функции.
15. Збянтэжаны Саўка 245 05.07.13 14:24 Сейчас в теме
ок, ПоместитьРезультатСКДвТаблицуЗначений() уже есть, но нужна еще ПолучитьОписаниеТипаБезПустых()
16. Yashazz 4707 05.07.13 21:40 Сейчас в теме
(15) Она совсем простая и необязательная:
Функция ПолучитьОписаниеТипаБезПустых(рОписТипов) Экспорт
мТипов=Новый Массив;
Для каждого рТип Из рОписТипов.Типы() Цикл
Если рТип=Тип("Неопределено") или рТип=Тип("NULL") или рТип=Неопределено или рТип=Null Тогда Продолжить КонецЕсли;
мТипов.Добавить(рТип);
КонецЦикла;
Возврат Новый ОписаниеТипов(мТипов,рОписТипов.КвалификаторыЧисла,рОписТипов.КвалификаторыСтроки,рОписТипов.КвалификаторыДаты);
КонецФункции
17. Abadonna 3958 09.07.13 17:08 Сейчас в теме
(0)
Пы.Сы. Не уверен, что за такую фиговинку имеет смысл плюсить.

Вот именно за это плюс и поставлю ;)
18. chmv 10.07.13 13:36 Сейчас в теме
Оставьте свое сообщение