СКД. Как объединить заголовки родительских группировок колонок в таблице




Принцип обмена данными из 1С с сайтом (на MySQL) и выдачи (публикации) этих данных по запросу.
PHP-Скрипт автоматической загрузки данных из файла данных в формате CSV в базу данных сайта работающего на WordPress.

В продолжение моей темы: 1С:Альфа-Авто Автосалон Автосервис: обмен с сайтом.
С помощью данного скрипта можно загружать в автоматическом режиме, по расписанию, данные сервисных книжек (ремонтов авто) из 1С:Альфа-Авто Автосалон Автосервис.
Также можно загружать данные в ручном режиме: для этого делается скрытая страница, где размещается специальная кнопка.
Комментарии размещенные внутри скрипта разъяснят логику и порядок действия.
Комментарии с "/////    echo" использовались для отладки.
Дополнительно создана таблица для журналирования результатов загрузки данных.
Скрипт включает в себя защиту от SQL инъекций (думаю безопасность соблюдена в полной мере).
В кратце:
1. Пишется скрипт, который запускает этот.
2. Создается регламентное задание в WordPress, по которому запускается скрипт из п.1. 
3. Этот скрипт осуществляет проверку на существование файла обмена в папке.
4. Если данные не новые, загрузка не производится.
5. Если данные новые, очищается таблица сервисных книжек.
6. Загружаются новые данные.

Собственно сам скрипт:

<?php // Полная загрузка сервисных книжек, создан 2025-01-05 12:44:55

global $wpdb2;
global $failure;
global $file_hist;

/////  echo '<H2><b>Старт загрузки</b></H2><br>';

$failure=FALSE;
//подключаемся к базе
$wpdb2 = include_once 'connection.php'; ; // подключаемся к MySQL
// если не удалось подключиться, и нужно оборвать PHP с сообщением об этой ошибке
if (!empty($wpdb2->error))
{
/////   echo '<H2><b>Ошибка подключения к БД, завершение.</b></H2><br>';
$failure=TRUE;
wp_die( $wpdb2->error );
}

$m_size_file=0;
$m_mtime_file=0;
$m_comment='';
/////проверка существования файлов выгрузки из 1С
////файл выгрузки сервисных книжек
$file_hist = ABSPATH.'/_1c_alfa_exchange/AA_hist.csv';
if (!file_exists($file_hist))
{
/////   echo '<H2><b>Файл обмена с сервисными книжками не существует.</b></H2><br>';
$m_comment='Файл обмена с сервисными книжками не существует';
$failure=TRUE;
}

/////инициируем таблицу лога
/////если не существует файла то возврат и ничего не делаем
if ($failure){
///включает защиту от SQL инъекций и данные можно передавать как есть, например: $_GET['foo']
/////   echo '<H2><b>Попытка вставить запись в лог таблицу</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>$m_mtime_file,'last_size_upload'=>$m_size_file,'comment'=>$m_comment));
wp_die();
/////    echo '<H2><b>Возврат в начало.</b></H2><br>';
return $failure;
}
/////проверка лога загрузки, что бы не загружать тоже самое
$masiv_data_file=stat($file_hist);   ////передаем в массив свойство файла
$m_size_file=$masiv_data_file[7];    ////получаем размер файла
$m_mtime_file=$masiv_data_file[9];   ////получаем дату модификации файла
////создаем запрос на получение последней удачной загрузки
////выбираем по штампу времени создания (редактирования) файла загрузки AA_hist.csv, $m_mtime_file

/////   echo '<H2><b>Размер файла: '.$m_size_file.'</b></H2><br>';
/////   echo '<H2><b>Штамп времени файла: '.$m_mtime_file.'</b></H2><br>';
/////   echo '<H2><b>Формирование запроса на выборку из лога</b></H2><br>';
////препарируем запрос
$text_zaprosa=$wpdb2->prepare("SELECT * FROM `vin_logs` WHERE `last_mtime_upload` = %s", $m_mtime_file);
$results=$wpdb2->get_results($text_zaprosa);

if ($results)
{   foreach ( $results as $r)
{
////если штамп времени и размер файла совпадают, возврат
if (($r->last_mtime_upload==$m_mtime_file) && ($r->last_size_upload==$m_size_file))
{////echo '<H2><b>Возврат в начало, т.к. найдена запись в логе.</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>$m_mtime_file,'last_size_upload'=>$m_size_file,'comment'=>'Загрузка отменена, новых данных нет, т.к. найдена запись в логе.'));
wp_die();
return $failure;
}
}
}
////если данные новые, пишем в лог запись о начале загрузки
/////echo '<H2><b>Попытка вставить запись о начале загрузки в лог таблицу</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>0, 'last_size_upload'=>$m_size_file, 'comment'=>'Начало загрузки'));

////очищаем таблицу
$clear_tbl_zap=$wpdb2->prepare("TRUNCATE TABLE %s", 'vin_history');
$clear_tbl_zap_repl=str_replace("'","`",$clear_tbl_zap);
$results=$wpdb2->query($clear_tbl_zap_repl);
/////   echo '<H2><b>Очистка таблицы сервисных книжек</b></H2><br>';
if (empty($results))
{
/////   echo '<H2><b>Ошибка очистки таблицы книжек, завершение.</b></H2><br>';
//// если очистка не удалась, возврат
$failure=TRUE;
wp_die();
return $failure;
}

////загружаем данные
$table='vin_history';         // Имя таблицы для импорта
//$file_hist Имя CSV файла, откуда берется информация     // (путь от корня web-сервера)
$delim=';';          // Разделитель полей в CSV файле
$enclosed='"';      // Кавычки для содержимого полей
$escaped='\

49 Comments

  1. Saint

    Если операция производится над сформированным табличным документом, то причём здесь СКД?

    Reply
  2. Defor
    Проблема в СКД. При формировании в колонках таблицы вложенных группировок, родительский заголовок группировки, не объединяет заголовки дочерних группировок.

    Это решение призвано привести в порядок вертикальные группировки в СКД. Если этот вариант можно использовать еще где нибудь я не против.

    Reply
  3. malina-da

    Спасибо за решение, сделал и для горизонтальных и вертикальных ячеек.

    Reply
  4. doronin70

    Классно работает и для вертикальных и для горизонтальных ячеек. А у кого получилось одновременное объединение горизонтальных и верткальных? Например объединенная ячейка должна быть высотой 2 строки и шириной 3 колонки ?

    Reply
  5. free-lancer-2018

    В платформе 8.2 уже все объединяет, если грамотно настроить

    Reply
  6. serg_gres

    (5) adminfo2002, серьезно?

    Пример в студию.

    Reply
  7. serg_gres

    Вопрос автору: объединятся ли смежные ячейки ресурсов с одинаковым значением?

    Reply
  8. serg_gres

    (8) adminfo2002, ждю…

    Reply
  9. Defor

    (7) serg_gres, не понял, зачем объединять ячейки ресурсов? Если речь идет об общей родительской группировке этих ресурсов, то да объединятся.

    Reply
  10. Defor

    (5) adminfo2002, действительно, было бы очень интересно посмотреть на новое решение …

    Reply
  11. bus1ka

    Единственное найденное решение, на мой взгляд. И в 8.2 ничего подобного, как ни настраивай…

    Reply
  12. bus1ka

    (8) adminfo2002, и я жду… )) передо мной сейчас такая же задача. Но как не пыталась, простой настройкой не обошлась

    Reply
  13. sdv88

    мне наоборот надо чтобы родительская группировка не объединялась.. версия платформы 8.2.14.540. есть какая то настройка?

    Reply
  14. Defor

    А можно поподробнее описать, в каком виде вы хотите видеть родительские группировки?

    Reply
  15. sdv88

    как в публикации на картинке где без объединения(покупатели или поставщики). чтобы над каждой колонкой было название верхней группировки. то есть начальный вариант в этой публикации

    Reply
  16. Defor

    Смотрите скриншот «Настройки СКД»

    Reply
  17. AlexO

    (8) adminfo2002,

    ок, сейчас потрачу полчасика на пример

    Полгода прошло… у вас там временная аномалия? 🙂

    (16) sdv88,

    как в публикации на картинке где без объединения

    «А вот тут немного снять.. здесь подчистить… тут отрезать…и вот изящная ложка готова!»

    «Не, мне баклушу как сделать?!…»

    Кто ж поймет этих 1сников…

    Reply
  18. AlexO

    (1) Saint,

    над сформированным табличным документом, то причём здесь СКД?

    Вы знаете способ обработать СКД напрямую?

    Reply
  19. Saint

    (19) AlexO, C добрым утром! Вопрос по комментарию трёхгодичной давности? Тем не менее попытаюсь ещё раз сформулировать: какое отношение описанный алгоритм имеет к СКД? Здесь обрабатывается уже сформированный табличный документ. А как он был сформирован для описанного алгоритма дело десятое. Точно также можно обрабатывать табличный документ, сформированный вручную.

    Reply
  20. Defor

    Если данное решение можно применить где-то еще я буду только рад. Но в первую очередь это будет полезно пользователям СКД.

    Reply
  21. AlexO

    (20) Saint,

    А как он был сформирован для описанного алгоритма дело десятое

    Как раз наипервейшее. Потому как СКД настолько закрытый и запутанный механизм, что любое осмысленное вмешательство в него, дающее уверенный результат — уже огромный плюс.

    Reply
  22. kurmanov

    Спасибо! Полезная вещь, очень помогла.

    Reply
  23. Evgeniy

    работает только при выводе одного показателя в отчете.

    Если делаешь вывод более одного показателя, то объединение не срабатывает:(

    Reply
  24. Defor

    Спасибо за замечание, добавлен доработанный пример для 8.2.

    Reply
  25. lamdth

    Подскажите, если у меня нет кнопки сформировать, и форма рисуется стандартно, как мне использовать данную процедуру?

    Reply
  26. KliMich

    Спасибо!

    Возьму на вооружение.

    Reply
  27. Defor

    (26) lamdth,

    На форме создайте кнопку, прицепите к ней свой обработчик, текст можно подсмотреть где угодно. Например выдрать из моего отчета этой темы. Пример есть в книге «Руководство разработчика» гл 10.4.

    Reply
  28. the1

    Нечто подобное методами СКД. Не так красиво, правда http://www.forum.mista.ru/topic.php?id=637203

    Reply
  29. rborovikov

    А как сделали рисунок «Имеем отчет вида»???

    Reply
  30. Defor

    (30) rborovikov,

    На скриншоте «Настройки СКД» показан пример настроек или можно скачать приложенный отчет и там посмотреть.

    Reply
  31. andreich_ru

    Спасибо, помогло.

    Я при использовании немного дополнил функцию ОбъединятьЯчейки():

    Функция ОбъединятьЯчейки(ТабДок, индСтр, индКол)
    
    ТекстДляОбъединения = Новый Массив;
    ТекстДляОбъединения.Добавить(«Начальный остаток»);
    ТекстДляОбъединения.Добавить(«Приход»);
    ТекстДляОбъединения.Добавить(«Расход»);
    ТекстДляОбъединения.Добавить(«Конечный остаток»);
    
    Ячейка = ТабДок.Область(индСтр, индКол);
    ЯчейкаСлед = ТабДок.Область(индСтр, индКол+1);
    
    Если ПустаяСтрока(Ячейка.Текст) Тогда
    
    Возврат Ложь
    
    //Проверяем, чтобы текст был одним из указанных в ТекстДляОбъединения
    ИначеЕсли ТекстДляОбъединения.Найти(Ячейка.Текст) <> Неопределено И Ячейка.Текст = ЯчейкаСлед.Текст И Ячейка.Имя = «R»+индСтр+»C»+индКол Тогда
    
    Возврат Истина;
    
    Иначе
    
    Возврат Ложь
    
    КонецЕсли;
    
    КонецФункции
    

    Показать

    Добавил массив, в котором указал конкретные строки, которые должны быть в ячейках для объединения. Потому что исходный вариант иногда объединял не то, что нужно.

    Reply
  32. Leits

    скачал для 8.2, процедуры в модуле форме вообще не прорабатываются, т.е. удалил вообще форму — результат тот же

    p.s. допёр — сделано в обычных формах, хотя написано 8.2 думал на управляемых

    Reply
  33. Maximysis

    (8) adminfo2002, Конечно пiZдеть проще)

    Reply
  34. anig99

    Спасибо за алгоритм. Есть небольшое дополнение. Чтобы сделать именно шапку на колонками, а не просто объединить одинаковые ячейки и не оставлять итоги по группировке слева от колонок, то можно использовать группировку полей. Вот как у меня на рисунке. Тогда при использовании доп. кода по объединению ячеек можно получить красивую таблицу с шапками колонок.

    Reply
  35. graZy

    Наткнулся случайно.

    я чего-то не понял (может быть реализация связана с «-5» лет назад? или связано с особыми требованиями к формату отчета)

    потому как ну нет проблемы вроде с отображением группировок

    настройка «расположение группировок — вместе» дает красивый результат (ну на платформе 8.3. точно, при том что по умолчанию подразумевает «вместе» и ничего настраивать не нужно)

    Reply
  36. Гость

    Спасибо автору!

    Reply
  37. Defor

    (37) graZy,

    Сейчас проверил на платформе 8.3.4.465 (другой под рукой не было), проблема осталась.

    з.ы.

    Проверил на 8.3.5.1383 — результат тот же. (Для проверки можно использовать отчет из загрузок например «Объединяемые заголовки для 8.2» и закомментировать вызов «ОбработатьЗаголовки(…)»

    Reply
  38. graZy

    (39)

    (39)

    8.3.5.1119

    (экперементировал на своем отчете)

    рис 1 «расположение полей группировок вместе»

    рис 2 «расположение полей группировок отдельно»

    Reply
  39. Defor

    (40) graZy,

    На втором вашем рисунке как раз нужно добиться общей шапки для «кварталов» и «полугодий»

    Reply
  40. graZy

    так а чем отчет на первом рисунке не устраивает??? (зачем городить рисунок 2 с объединением) — как писал ранее, если это только не требования к форме отчета (тогда понятно зачем) — в остальных случаях рис1 по мне так идеально подходит (и все стандартными средствами СКД)

    Reply
  41. demientut
    Reply
  42. aleksey_1984

    (43) demien@tut.by, (43) demien@tut.by,

    Добрый день.

    Что бы настройки брались пользовательские надо

    вместо этого:

    Настройки = КомпоновщикНастроек.Настройки;

    Надо написать это:

    Настройки= КомпоновщикНастроек.ПолучитьНастройки();

    Взял отсюда:

    http://1cskd.ru/2012/03/programmnoe-vypolnenie-skd-i-polzovatelskienastrojki/

    Reply
  43. AllexSoft

    Актуально, но нормально не работает (( мне например итог по группировке не нужен вообще (на скриншоте из примера первая колонка Покупатели), так еще и 3 группировки в колонках! как этому хозяйству ума дать ? может 1С что нибудь стандартное придумало все же ?

    Reply
  44. dyuha

    Требовалось объединить более одного уровня группировки. Наваял вот. Пользуйтесь, кому надо. Спасибо авторам.

    Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
    СтандартнаяОбработка = Ложь;
    КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
    Настройки = КомпоновщикНастроек.ПолучитьНастройки();
    МакетКомпоновкиДанных = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки, ДанныеРасшифровки);
    ПроцессорКомпоновкиДанных = Новый ПроцессорКомпоновкиДанных;
    ПроцессорКомпоновкиДанных.Инициализировать(МакетКомпоновкиДанных, , ДанныеРасшифровки, Истина);
    ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
    ПроцессорВывода.УстановитьДокумент(ДокументРезультат);
    ПроцессорВывода.Вывести(ПроцессорКомпоновкиДанных, Истина);
    ДокументРезультат = ОбработатьЗаголовки(ДокументРезультат);
    КонецПроцедуры
    
    Функция ОбъединятьЯчейки(ТабДок, индСтр, индКол)
    Ячейка = ТабДок.Область(индСтр, индКол);
    ЯчейкаСлед = ТабДок.Область(индСтр, индКол+1);
    Если ПустаяСтрока(Ячейка.Текст) Тогда
    Возврат ложь
    ИначеЕсли
    Ячейка.Текст = ЯчейкаСлед.Текст
    И Ячейка.Верх = Ячейка.Низ И ЯчейкаСлед.Верх = ЯчейкаСлед.Низ Тогда
    Возврат Истина;
    Иначе
    Возврат ложь
    КонецЕсли;
    КонецФункции
    
    Функция ОбработатьЗаголовки(ТабДок)
    ВысотаФ = ТабДок.ФиксацияСверху;
    ШиринаФ = ТабДок.ФиксацияСлева;
    ОбъединяемаяОбласть = Неопределено;
    Для индСтр = -ВысотаФ По -1 Цикл
    Если ТабДок.Область(-индСтр,1).Текст = «» Тогда
    Возврат ТабДок;
    КонецЕсли;
    НачальнаяКолонка = 0;
    Для индКол=ШиринаФ+1 По ТабДок.ШиринаТаблицы Цикл
    Если ОбъединятьЯчейки(ТабДок, -индСтр, индКол) Тогда
    Если не НачальнаяКолонка Тогда
    НачальнаяКолонка = индКол;
    КонецЕсли;
    ИначеЕсли НачальнаяКолонка Тогда
    ТекстЗаголовка = ТабДок.Область(-индСтр, индКол).Текст;
    ОбъединяемаяОбласть = ТабДок.Область(-индСтр, НачальнаяКолонка, -индСтр, индКол);
    ОбъединяемаяОбласть.Объединить();
    ОбъединяемаяОбласть.ГоризонтальноеПоложение = ГоризонтальноеПоложение.Центр;
    ОбъединяемаяОбласть.Текст = ТекстЗаголовка;
    НачальнаяКолонка = 0;
    Иначе
    НачальнаяКолонка = 0;
    КонецЕсли;
    КонецЦикла;
    КонецЦикла;
    Возврат ТабДок;
    КонецФункции

    Показать

    Выбираем только шапку и объединяем все, что можем.

    Reply
  45. Yashazz

    Блин. Обрадовался было, что есть способ сделать это средствами СКД, а тут опять свинство под названием «поячейно курочим готовый моксель». Тьфу. Аж минуснуть захотелось.

    Reply
  46. pirm2

    Так что ли?

    Reply
  47. Volfy
    Reply
  48. Volfy

    Спасибо за полезную тему, очень помогло объединить заголовки месяцев над неделями.

    Reply
  49. Hans

    (8) ждемс…

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *