Программное создание динамического списка на управляемой форме. (Динамическое создание динамического списка). Готовая процедура + обработка — демонстрационный пример




Принцип обмена данными из 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='\

25 Comments

  1. palsergeich

    Код приятный.

    Нужно только понимать, после ПриСозданииНаСервере добавлениеудаление реквизитов — очень дорогая операция…

    Reply
  2. rpgshnik

    (1) спасибо, учту про стоимость операции.

    Reply
  3. pm74

    (0) полезная публикация , спасибо

    Reply
  4. Denis Nsk

    Спасибо большое!

    Лично для меня, очень полезная публикация. Отметил в избранное на будущее.

    Спасибо за потраченное время и подробное описание!

    Reply
  5. Batchir

    Спасибо!

    Появились хотелки к статье)))

    1. Не хватает в статье описания того, как в таком случае устанавливать параметры динамического списка.

    В «типовом» варианте это делается на сервере:

    МойСписок.Параметры.УстановитьЗначениеПараметра(«МойПараметр»,ЗначениеМоегоПараметра);

    При программном создании «МойСписок» отсутствует и будет валиться на синтаксис.

    2. Было бы замечательно если бы описали формирование колонок динамического списка.

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

    Reply
  6. rpgshnik

    (5) Дельные хотелки, когда есть обратная связь особенно интересно.

    1. Как-то я забыл про Параметры, у меня их не было, я как-то отборами привык уже))

    Добавлю в саму процедуру, обновлю чуть позже.

    2. Я конечно не хотел преследовать супер-универсальности, возможно подумаю, но не обещаю.

    Да и цель была именно линейные списки. Возможно стоит проще постобработкой список скорректировать группировками… я и отборы не добавляю, так это точно перебор)))

    Reply
  7. Vlad_2008

    Ну синтаксис можно и обойти: ЭтаФорма[«МойСписок»].Установить …

    А вот как быть с характеристиками (а-ля «доп. реквизиты» в типовых)? Их можно задать в конфигураторе в свойствах объекта, тут получается что надо все вставлять в запрос для каждой формы объекта.

    И, может кто-нибудь знает, как программно добавить колонку с характеристикой, как будто пользователь вынес на форму поле Ссылка.[Мой доп реквизит] ? В запросе-то поля нет, а вот в «Изменить форму — Ссылка — Добавить поля» — есть.

    Reply
  8. rpgshnik

    (7) интересно, но не сталкивался( вообще мало работаю с новыми конфигами, увяз в совместимости 8.2.13

    Reply
  9. rpgshnik

    (5) функционал в процедуру по передачи параметров добавил, публикацию обновил.

     //Заполняем параметры если они были указаны
    Если ПараметрыЗапроса <> Неопределено Тогда
    Для Каждого Параметра из ПараметрыЗапроса Цикл
    РеквизитДинамическийСписок.Параметры.УстановитьЗначениеПараметра(Параметра.Ключ,Параметра.Значение);
    КонецЦикла;
    КонецЕсли;

    на второй ваше предложение, используйте группировки, ни их не отборы в процедуру добавлять не планирую

    Reply
  10. rpgshnik

    (7) Мне кажется я понял вас. Я почему-то подумал про ПВХ и дополнительные Свойства Объектов.

    А вы говорите просто об реквизитах ссылочного объекта.

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

    Пример, запрос динамического списка:

    ВЫБРАТЬ
    РеализацияТоваровУслуг.Дата,
    РеализацияТоваровУслуг.Номер,
    РеализацияТоваровУслуг.Организация,
    РеализацияТоваровУслуг.Контрагент
    ИЗ
    Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг

    Код обработки события, к примеру по нажатию кнопки:

    &НаКлиенте
    Процедура ДобавитьКолонку(Команда)
    ДобавитьКолонки();
    КонецПроцедуры
    
    &НаСервере
    Процедура ДобавитьКолонки()
    НоваяКолонкаТаблицы = Элементы.Вставить(«дСписокКонтрагентИНН»,Тип(«ПолеФормы»),Элементы.дСписок,Элементы.дСписокОрганизация);
    НоваяКолонкаТаблицы.ПутьКДанным = «дСписок.Контрагент.ИНН»;
    НоваяКолонкаТаблицы.Заголовок = «ИНН»;
    НоваяКолонкаТаблицы = Элементы.Вставить(«дСписокКонтрагентКПП»,Тип(«ПолеФормы»),Элементы.дСписок,Элементы.дСписокОрганизация);
    НоваяКолонкаТаблицы.ПутьКДанным = «дСписок.Контрагент.КПП»;
    НоваяКолонкаТаблицы.Заголовок = «КПП»;
    НоваяКолонкаТаблицы = Элементы.Добавить(«дСписокОрганизацияИНН»,Тип(«ПолеФормы»),Элементы.дСписок);
    НоваяКолонкаТаблицы.ПутьКДанным = «дСписок.Организация.ИНН»;
    НоваяКолонкаТаблицы.Заголовок = «ИНН»;
    КонецПроцедуры
    

    Показать

    Обработка прилагается:

    Reply
  11. Batchir

    (9) Да я то что ))) Если мне нужно будет, то конечно буду использовать (и сам смогу дописать). Это хотелка была для полноты статьи: программное создание, размещение динамического списка есть, а программного управления колонками нет (размещение в группах вместе, горизонтально …).

    В любом случае ещё раз спасибо. Саму функцию закинул в шаблоны, думаю пригодиться.

    Reply
  12. rpgshnik

    (11) ок, понял))

    Reply
  13. Vlad_2008

    (10)

    Не, я как раз именно про ПВХ. С реквизитами все понятно и просто. А вот как с характеристиками — вопрос?

    В отчете на СКД я могу добавить поле программно (например в отбор) так:

    Новый ПолеКомпоновкиДанных(«Организация.[Связанный контрагент]»);

    Дин. список тоже на СКД, характеристики можно описать в свойствах объекта или в конструкторе запроса.

    Ес-но, можно программно задать текст запроса с характеристиками, после чего пользователь сможет вынести на форму колонку с произвольной характеристикой.

    Так вот и не могу понять можем ли мы программно создать такую колонку? Как указать путь к данным?

    Reply
  14. antont

    (13) Можно и без изменения запроса.

    В качестве пути к данным указывается имя доступного поля выбора компоновщика настроек динамического списка.

    ЕМаил = Элементы.Добавить(«ЕМаил»,Тип(«ПолеФормы»),Элементы.Список);
    ЕМаил.ПутьКДанным =»Список.Ссылка.[E-mail]»;
    Reply
  15. Vlad_2008

    (15)

    Работает, спасибо!

    Основная проблема была в том, что невозможно было определить колонку, которую пользователь добавил через «Изменить форму» … ну я никак не смог до нее достучаться … в элементах формы ее тоже нет.

    С добавленной программно колонкой все хорошо, вот только в настройках формы она «автоматом» не сохраняется, но это уже другая история …

    Reply
  16. t0202

    Спасибо, успешно воспользовался Вашей процедурой.

    Заметил один недостаток.

    В массиве колонок желательно поменять местами заголовок и имя колонки.

    У меня заголовки колонок оказались с запятыми и пришлось это поправить.

    Reply
  17. rpgshnik

    (17) спасибо проверю

    Reply
  18. Daynestro07

    Дельная статья. Интересно только, есть ли способ получить массив выводимых колонок автоматически, из запроса?

    upd Уже неактуально

    Reply
  19. Vida

    (14) В отличие от автора статьи, у вас есть реквизит дСписок — динамический список. Поэтому в указанных вводных ваш код работать не будет, а именно

    дСписок.ТекстЗапроса = ТекстЗапроса;

    по причине из (5) часть 1

    А вот как это обойти — не придумала.

    Reply
  20. Vida

    (14) Так вы и есть автор! Нашла свою ошибку почему не могла параметры поменять. Создаю программно «СписокМой», а потом пытаюсь изменить параметры у «МойСписок».

    Все отлично работает

    &НаСервере
    Процедура СписокПриАктивизацииСтрокиНаСервере(ДатаР, НомерР, ИмяСписка)
    Если ЭтаФорма.Элементы.Найти(ИмяСписка) <> Неопределено Тогда
    РеквизитДинамическийСписок = ЭтаФорма[ИмяСписка];
    РеквизитДинамическийСписок.Параметры.УстановитьЗначениеПараметра(«НомерРег», НомерР);
    РеквизитДинамическийСписок.Параметры.УстановитьЗначениеПараметра(«ДатаРег», ДатаР);
    КонецЕсли;
    КонецПроцедуры
    
    &НаКлиенте
    Процедура СписокПриАктивизацииСтроки(Элемент)
    Сообщить(Элемент.ТекущиеДанные.Дата);
    СписокПриАктивизацииСтрокиНаСервере(Элемент.ТекущиеДанные.Дата, Элемент.ТекущиеДанные.Номер, «СписокМой»);
    КонецПроцедуры
    

    Показать

    Reply
  21. mikl79

    Большое спасибо автору!!!

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

    Единственное, мне нужно вывести динам.список на отдельную страницу.

    Reply
  22. mikl79

    немного доработал Процедуру — добавил еще один параметр, чтобы выводилось на страницу

    Reply
  23. rpgshnik

    (23) не смог понять, а параметра — «ДобавитьВ» не достаточно? Интересно.

    Reply
  24. mikl79

    (24), действительно воспользовался параметром «ДобавитьВ», но перед вызовом процедуры программно создал новую страницу

    Reply
  25. mikl79

    Не сразу заметил, что вы используете при добавлении элементов на форму процедуры Добавить и Вставить, в чем их разница — не понял.

    «Вставить» в отличие от «Добавить» только содержит 4-й параметр «Элемент» — Элемент, перед которым должен быть вставлен новый элемент. Если не указан, то элемент будет вставлен в конец.

    А так вроде делают одно и тоже, но это наверное вопрос риторический — разработчикам.

    Reply

Leave a Comment

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