Создание произвольной таблицы значений на форме в управляемом приложении программным способом




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

35 Comments

  1. seermak

    (0) Все просто — формируем через СКД нужные данные и выгружаем их реквизит формы «табличный документ» Расшифровку передаем через временное хранилище. Если надо предусмотреть параметры колонок или еще что-то делаем это в СКД

    Reply
  2. profiprog1c

    (1) seermak,

    Это все да, а если Ваша таблица должна получится путем сложных вычислений. Например Запрос — Обработка запроса — Еще Запрос — Обработка запроса, совмещение и расчет данных программно, тогда как?

    Reply
  3. seermak

    (2) Вы еще про перебор справочников вспомните) Все Ваши «Запрос — Обработка запроса — Еще Запрос — Обработка запроса» (если Вам нравится туда-сюда)загоняете в таблицу и используете, как внешние данные СКД

    Reply
  4. profiprog1c

    (3) seermak, Ну при чем тут перебор справочника. Я так понял, что Вы привыкли получать таблицы значений одним запросом. А я Вам говорю о том, что есть таблицы которые получаются вычислениями вне запросов и при чем тут СКД? И кстати, по поводу перебора справочника, иногда выгоднее сделать перебор, чем делать запрос.

    Reply
  5. Yashazz

    А ещё можно посмотреть, как эта задача давным-давно решена в УТ11 и БП3, и не изобретать велосипед.

    Статья неплохая, но опоздала года на полтора.

    Reply
  6. profiprog1c

    (5) Yashazz, С таким успехом можно все везде посмотреть. Статья предназначена для тех, кто столкнулся с такой задачей и не хочет где-то искать, а хочет сразу готовое. Каждому свое.

    Reply
  7. mammadov.zaur.77

    profiprog1c

    Спосибо.

    Мне очень помогло.

    А как Избавится от командного панеля.

    Например я хочу импортиромат из .тхт.

    В этом случе мне не нужно Командный панел таблицы.

    Заране спосибо.

    Reply
  8. profiprog1c

    (7) mammadov.zaur.77,

    В процедуре СформироватьТаблицуЗначений()

    В коде, где создаем объект «ТаблицаПолейВыбора» размещаем строку:

    ТаблицаПолейВыбора.ПоложениеКоманднойПанели = ПоложениеКоманднойПанелиЭлементаФормы.Нет;

    Командная панель будет не видна на форме после этого.

    Reply
  9. Cyberhawk

    У формы стоит АвтоматическоеСохранениеДанныхВНастройках = Истина.

    Вопрос: можно ли программно создать реквизит с галочкой «Сохранение»? Не путать со свойством «Сохраняемые данные», Я веду речь о галочке в таблице реквизитов формы в конструкторе (приложил картинку).

    Просто хочется, чтобы значения программно добавляемых реквизитов сохранялись средствами платформы при закрытии формы (ну, восстанавливать их придется уже самому, это понятно), не хочется писать это самостоятельно. Не нашел такой возможности ни в конструкторе, ни через точку.

    Reply
  10. Cyberhawk

    +(9) в общем, средствами платформы сохранять значения программно созданных реквизитов нельзя.

    Решение следующее: при открытии / перед закрытием / где удобно взводить свойство формы

    СохраняемыеВНастрой
    Reply
  11. denxp2009

    Автору огромное спасибо!!! Статья пошла в избранное 🙂

    Reply
  12. AndreykO

    Интересная технология. Вот только у меня одна из колонок типа «ЛюбаяСсылка» и не взлетает…((

    Reply
  13. profiprog1c

    (13) AndreykO,

    В статье я отмечал, что колонка таблицы значений должна иметь явный, конкретный тип, а вот «ЛюбаяСсылка» не является конкретным типом.

    Reply
  14. platon_

    Интересная статья, хотелось бы ещё посмотреть создание таблицы с числами, и возможностью последующего вывода итога в подвал.

    Reply
  15. kitaevay

    Пример рабочего кода на тему, на основе примера выше, ТЗ выгружается по результату запроса, Колонки автоматически создаются из колонок ТЗ

    Таблица на форме при этом получает колонку с флажком и остальные колонки нормально типизированы

      МассивТипаВыбора = Новый Массив;
    МассивТипаВыбора.Добавить(Тип(«ТаблицаЗначений»));
    ОписаниеТипаВыбора = Новый ОписаниеТипов(МассивТипаВыбора);
    МассивРеквизитов = Новый Массив;
    ИмяТЗ = «ТаблицаСегментов»; ЗаголовокТЗ = «Сегменты»;
    МассивРеквизитов.Добавить(Новый РеквизитФормы(ИмяТЗ, ОписаниеТипаВыбора, «», ЗаголовокТЗ));
    
    Запрос = Новый Запрос;
    Запрос.Текст =
    «ВЫБРАТЬ
    | ЛОЖЬ КАК Выб,
    | СегментыНоменклатуры.Ссылка КАК Сегмент
    |ИЗ
    | Справочник.СегментыНоменклатуры КАК СегментыНоменклатуры
    |ГДЕ
    | НЕ СегментыНоменклатуры.ПометкаУдаления
    |
    |УПОРЯДОЧИТЬ ПО
    | СегментыНоменклатуры.Наименование»;
    
    РезультатЗапроса = Запрос.Выполнить();
    ТЗ = РезультатЗапроса.Выгрузить();
    
    Для Каждого Колонка Из ТЗ.Колонки Цикл
    ТипыЗначений = Новый Массив;
    Для каждого тТип из Колонка.ТипЗначения.Типы() Цикл
    Если тТип <> Тип(«Null») Тогда
    ТипыЗначений.Добавить(тТип);
    КонецЕсли;
    КонецЦикла;
    МассивРеквизитов.Добавить(Новый РеквизитФормы(Колонка.Имя, Новый ОписаниеТипов(ТипыЗначений), ИмяТЗ));
    КонецЦикла;
    
    ЭтаФорма.ИзменитьРеквизиты(МассивРеквизитов);
    эТаблица = Элементы.Добавить(ЗаголовокТЗ, Тип(«ТаблицаФормы»), Элементы.ГруппаТовары);
    эТаблица.ПутьКДанным = ИмяТЗ;
    эТаблица.Отображение = ОтображениеТаблицы.Список;
    эТаблица.ПоложениеКоманднойПанели = ПоложениеКоманднойПанелиЭлементаФормы.Нет;
    эТаблица.ИзменятьСоставСтрок = Ложь;
    
    Для Каждого Колонка Из ТЗ.Колонки Цикл
    НовыйЭлемент = Элементы.Добавить(Колонка.Имя, Тип(«ПолеФормы»), эТаблица);
    НовыйЭлемент.ПутьКДанным = ИмяТЗ + «.» + Колонка.Имя;
    Если Колонка.Имя = «Выб» Тогда
    НовыйЭлемент.Вид = ВидПоляФормы.ПолеФлажка;
    Иначе
    НовыйЭлемент.Вид = ВидПоляФормы.ПолеВвода;
    НовыйЭлемент.КнопкаОчистки = Ложь;
    НовыйЭлемент.КнопкаВыбора = Ложь;
    НовыйЭлемент.РедактированиеТекста = Ложь;
    НовыйЭлемент.КнопкаОткрытия = Истина;
    КонецЕсли;
    КонецЦикла;
    
    ЭтаФорма.ЗначениеВРеквизитФормы(ТЗ, ИмяТЗ);
    

    Показать

    Reply
  16. profiprog1c

    (16) kitaevay,

    Не вводите никого в заблуждение, вы типы присваиваете каждой колонке. И логично, что если вы из запроса грузите данные в ТЗ, то колонки ТЗ будут типизированы, а если они не типизированы вы им пытаетесь присвоить тип. А в моем примере речь шла о колонах в ТЗ, которые создаются руками.

    Reply
  17. Yashazz

    (17) да можно и не типизировать. Вы тут поднагнали пурги, мол, обязательно… Может, в старых релизах и было обязательно, да только всё меняется)

    Начиная с 8.3.5, реквизит формы, в т.ч. подреквизит табчасти («колонка реквизита»), неважно, таблица ли значений или дерево, добавлена ли кодом или в конфигураторе — прекрасно может иметь тип «Произвольный». Делается очень просто:

    Функция ПолучитьПустоеОписаниеТипов() Экспорт
    мТипов=Новый Массив;
    квЧисла=Новый КвалификаторыЧисла(0,0);
    квСтроки=Новый КвалификаторыСтроки(0);
    квДаты=Новый КвалификаторыДаты(ЧастиДаты.ДатаВремя);
    Возврат Новый ОписаниеТипов(мТипов,квЧисла,квСтроки,квДаты);
    КонецФункции
    
    МойРеквизит=Новый РеквизитФормы(«МойТест»,ПолучитьПустоеОписаниеТипов());
    

    Показать

    Именно таким образом можно сделать динамически реквизит, в т.ч. колонку таблицы значений, которая будет вести себя как имеющая «Произвольный» тип. Только что проверено на 8.3.6.2014

    Так что не зря я ворчал, публикация не вполне точна в своих утверждениях.

    Reply
  18. profiprog1c

    (18) Yashazz,

    А где у вас в вашем куске кода Произвольный тип???

    От того что вы свою функцию назвали ПолучитьПустоеОписаниеТипов(), она пустой не стала.

    В вашем коде я вижу мультитип, число, строка, дата — это да, но пустого в упор не заметил.

    Reply
  19. Yashazz

    (19) Повторяю — именно такое описание типов и даёт эффект работы с реквизитом, как будто он произвольного типа. Собственно, в понятиях 1С именно приведённое мной описание типов и есть произвольный тип. Вы создайте на форме реквизит произвольного типа и посмотрите на него, хоть в отладчике, хоть как. Всё просто.

    Reply
  20. profiprog1c

    (20) Yashazz,

    Причем тут эффект работы, не играйте словами. Вы колонку тоже задаете как параметр.

    В обычном приложении можно создать было колонку:

    ТЗ.Колонки.Добавить(«Контрагент») и все — колонка готова.

    В вашем примере такое есть? Нету.

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

    Reply
  21. Yashazz

    (21) а, так вы о том, чтобы вообще ничего не писать… Тогда пардон, неверно вас понял. Я всего лишь к тому, что эффект одинаковый — что ничего писать не надо, что мой вариант писать надо — в итоге результат тот же. А мы ведь на результат работаем, верно?

    Reply
  22. profiprog1c

    (22) Yashazz,

    Результат верен, но неверна терминология.

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

    А нужно ее параметризировать, по сути описать. Присвоить колонке пустышку, просто имя, без параметра, как в обычном приложении, нельзя.

    Если 8.3.6 есть возможность задать мультипараметр в колонку, да без проблем, но его тоже надо задать, сам он не появляется.

    Это все вопросы вкуса программирования конкретного программиста, но пустышку задать нельзя, как это было в обычном.

    Reply
  23. Novichok777

    Все хорошо, но код на 8.3.6.2014 выдает ошибку в строке «НовыйЭлемент = Элементы.Добавить(Колонка.Имя, Тип(«ПолеФормы»), ТаблицаПолейВыбора);» (а у (16) в строке «НовыйЭлемент = Элементы.Добавить(Колонка.Имя, Тип(«ПолеФормы»), эТаблица);».

    Ошибка одна и та же: Недопустимое значение параметров!

    Убил не один час, чтобы понять в каком параметре ошибка. Ответ как всегда прост.

    В первом параметре НЕЛЬЗЯ указывать одно и тоже имя с реквизитом. Т.е. вот так если записать «НовыйЭлемент = Элементы.Добавить(ИмяТЗ + Колонка.Имя, Тип(«ПолеФормы»), эТаблица); «, то все Ок.

    Удачи. Спасибо за статью.

    Reply
  24. profiprog1c

    (24) Novichok777,

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

    А вот то, что тут комментаторы пишут и какие-то куски кода лепят в том же (16), я это не тестирую, и оно да, может не работать.

    Reply
  25. andrewrocker

    Коллеги,

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

    Процедура ЗаполнитьКолонки(Элемент)

    Запрос = Новый Запрос;

    Запрос.Текст = «ВЫБРАТЬ

    | ОптовыеЗаказчики.Заказчик,

    | ОптовыеЗаказчики.ФормироватьСводныйЗаказ

    |ИЗ

    | РегистрСведений.ОптовыеЗаказчики КАК ОптовыеЗаказчики

    |ГДЕ

    | ОптовыеЗаказчики.ФормироватьСводныйЗаказ = ИСТИНА»

    ;

    Результат = Запрос.Выполнить();

    Обход = Результат.Выбрать();

    Пока Обход.Следующий() Цикл

    Таб.Колонки.Добавить(Обход.Заказчик,,Обход.Заказчик,50);

    КонецЦикла;

    КонецПроцедуры

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

    Reply
  26. jaroslav.h

    (16) kitaevay, ну, мужик! ну благодарю! То, что надо!

    Reply
  27. zels

    Интересно, как сделать, чтобы по клику на заголовке колонки таблица сортировалась по этой колонке.

    Reply
  28. ASWar

    (24), подтверждаю, имена д.б. уникальными. Имя ТЗ лучше всегда добавлять в данном случае.

    Reply
  29. elvira17

    (16) Спасибо огромное! Все работает! Платформа 8.3.8.2167

    Reply
  30. kitaevay

    (17) Я не знаю о каком заблуждении идет речь, в моём описание всё по существу, я не обещаю большего, чем есть в предложенном коде. Есть некоторый минимум знаний, который необходим, чтобы воспользоваться моим кодом, который подразумевает, что заблуждения тут маловероятны, а если они вдруг случились, то это поправимо типизацией колонки при добавлении оной вручную 😉

    PS я поставил вам лайк, т.к. ваша публикация мне помогла и указал это в своём комментарии, не вижу причин для мелочной ревности. Peace Love Heaviness.

    Reply
  31. papche

    Спасибо! Я в восторге))

    Reply
  32. virtex3

    Кто-нибудь, напишете как в подвале итоговую сумму вывести, пожалуйста

    Reply
  33. dolter

    (33) Ну, если еще не поздно )

    Элементы.ТабличнаяЧасть1.Подвал = Истина;
    Элементы.ТабличнаяЧасть1Б.ПутьКДаннымПодвала = «Объект.ТабличнаяЧасть1.ИтогБ»;
    Reply
  34. user1226970

    Пардон,только учусь у меня при написании кода ,выходит ошибка {ВнешняяОбработка.ВыгрузкаПрайсаXML.Форма.Форма.Форма(195)}: Ошибка при вызове метода контекста (ИзменитьРеквизиты)

    ИзменитьРеквизиты(МассивРеквизитов);

    по причине:

    Неуникальное имя реквизита. Имя: «ТаблицаРеквизитов»

    Что делаю не так?

    Reply
  35. sidelev08

    (35) По всей видимость у вас на форме уже есть реквизит с таким наименованием.

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

    Reply

Leave a Comment

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