Универсальный инструмент для переноса данных через табличный документ (УФ)




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

31 Comments

  1. Rustig

    (0) интересная работа!

    Reply
  2. Anyu-n

    Добрый день, а рабтает только для идентичных конфигураций? Для переноса пару доков их БП2 в БП3 пойдет, как думаете?

    Reply
  3. json

    (2) В БП2 и БП3 структура данных разная. Этим инструментом вашу задачу скорее всего не решить.

    Reply
  4. user625761_elena-proxorova

    Добрый день. А между Промышленной безопасностью и ЗУП 3, выполнит перенос?

    Reply
  5. json

    (4) Инструмент переносит данные между конфигурациями с идентичной или почти идентичной структурой данных.

    Если в конфигураторе объекты называются одинаково и поля этих объектов называются одинаково и имеют одинаковый тип, то они перенесутся, в противном случае — не перенесутся или будет ошибка.

    Касательно конфигурации Промышленная безопасность — сказать ничего не могу, т.к. с такой не работал

    Reply
  6. json

    (6)

    Да, на строках неограниченной длины в регистрах падала ошибка.

    Исправил. Скачайте еще раз (повторное скачивание в течение первых дней — бесплатное)

    Reply
  7. impron

    ERP 2.4.6.188 (демонстрационная база)

    При попытке переноса справочника Номеклатура (нажимаем кнопку Выгрузить):

    Выдает окно с сообщением: Получение элемента по индексу для значения не определено

    В сообщения выводит:

    {ВнешняяОбработка.ПереносДанных.МодульОбъекта(579)}: Значение не является значением объектного типа (Метаданные):[]

    Reply
  8. json

    (8) Благодарю за подробное описание ошибки.

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

    Выложил исправленную версию

    Reply
  9. json

    Выложил версию с исправлениями.

    Если вы скачивали ранее и хотите получить обновленную версию — напишите мне в личку

    Reply
  10. impron

    ERP 2.4.6.188 (демонстрационная база).

    На обновленной обработке заработало.

    Но при переносе данных не сохранило структуру (см. приложенный файл).

    Левая часть — структура в демонстрационной базе.

    Правая часть — структура в базе в которую переносили номенклатуру.

    Папки второго уровня переехали на первый уровень.

    Reply
  11. json

    (11)

    Мне не удалось повторить ошибку. Структура папок переносится корректно.

    Могу предположить, что группы второго уровня ЯВНО не добавлялись в выгрузку. То есть выгружались элементы, которые находятся в этих группах. При этом когда при загрузке создавались элементы, создались и их родители. Но т.к. родители явно в выгрузке не были указаны, то они создались в корне.

    В таком случае если выгрузить-загрузить только группы, то иерархия встанет на место

    Если мое предположение не верно, то уточните, пожалуйста:

    -версию платформы

    -название справочника

    -как выгружали загружали: выгрузили полностью справочник и загрузили в пустой справочник в другой базе или как-то иначе?

    Reply
  12. German_Tagil

    посмотрим — на обычных формах таб документ сильно выручал

    Reply
  13. Mx00

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

    Reply
  14. greywind

    Бух 3.0 (платформа 8.3.12, Релиз 3.0.67)

    Ошибка:

    ТИП: РегистрБухгалтерии.Хозрасчетный.Ошибка при создании данных из макета в строках с 8 по 12 : {ВнешняяОбработка.ПереносДанных.МодульОбъекта(1190)}: Ошибка компиляции при вычислении выражения или выполнении фрагмента кода: {(1,1)}: Процедура или функция с указанным именем не определена (ЗаполнитьИзТаблицыЗначений_РегистрБухгалтерии)

    Reply
  15. greywind

    Ошибка срабатывает при Загрузке документа с движениями.

    Reply
  16. json

    (16) Спасибо за подробное описание.

    Выложил исправленную версию

    Reply
  17. bulas

    Отбор по организации (или по контрагенту) есть?

    Reply
  18. json

    (18)

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

    Reply
  19. jack19

    1С:Предприятие 8.3 (8.3.13.1513)

    Управление производственным предприятием, редакция 1.3 (1.3.122.2)

    Обработка просто не открывается.

    Reply
  20. json

    (20) Обработка запускается только в режиме управляемого приложения.

    УФ в названии — управляемые формы.

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

    Есть два способа воспользоваться обработкой в конфигурации УПП:

    1. Зайти в базу УПП в режиме управляемого приложения и открыть ее как внешнюю обработку

    2. Встроить обработку в конфигурацию и не забыть включить флаг использования управляемых форм в режиме обычного приложения. После этого открыть обработку как внутреннюю

    Reply
  21. jack19

    (21) Извините, я в 8-ке можно сказать дилетант. Как запустить базу в режиме управляемого приложения?

    Reply
  22. json

    (22) самый простой способ из конфигуратора.

    В конфигураторе в верхнем меню выбрать: Отладка — Начало отладки — Тонкий клиент: начать отладку

    Reply
  23. jack19

    (23) Спасибо. Попробовал, запускается.

    Reply
  24. jack19

    (23) Прошу прощения, попробовал перенести справочник «Номенклатура» в одной базы в другую (однотипную). Получилось как-то криво, все папки (пустые) и элементы оказались в одном уровне. При выгрузке ставил в настройках глубину выгрузки = 4, при загрузке ничего не ставил. Наряду со справочником «Номенклатура» программа выгрузила ряд других справочников («Контрагенты», «Виды номенклатуры», «Номенклатурные группы» и др.), ссылки на элементы которых, видимо были в справочнике «Номенклатура». По окончании загрузки программа выдала следующие сообщения:

    ТИП: Справочник.ЕдиницыИзмерения.Ошибка при создании данных из макета в строках, начиная с 42 : {ВнешняяОбработка.ПереносДанных.МодульОбъекта(1154)}: Ошибка при вызове метода контекста (Вставить): Задано неправильное имя атрибута структуры

    ТИП: Справочник.Номенклатура.Ошибка при создании данных из макета в строках, начиная с 43 621 : {ВнешняяОбработка.ПереносДанных.МодульОбъекта(1154)}: Ошибка при вызове метода контекста (Вставить): Задано неправильное имя атрибута структуры

    ТИП: Справочник.ГруппыОбъектовРабот.Ошибка при создании данных из макета в строках, начиная с 89 427 : {(1)}: Поле объекта не обнаружено (ГруппыОбъектовРабот)

    ТИП: Справочник.НоменклатурныеГруппы.Ошибка при создании данных из макета в строках, начиная с 89 449 : {(1)}: Поле объекта не обнаружено (ГруппыОбъектовРабот)

    Что я не так сделал?

    Reply
  25. json

    (25) попробуйте сделать следующее:

    Перенесите группы номенаклатуры отдельно.

    1. Установите отбор ЭтоГруппа = Истина,

    2. уровень выгрузки = 0, затем

    3. нажать кнопку Выбрать все

    4. выгрузить

    5. загрузить в другую базу

    Потом уже грузите нужные элементы номенклатуры с уровнем = 1

    Также внимательно прочитайте в публикации пункт Требования

    Данные загружаются в режиме ОбменДанными.Загрузка = Истина. Это значит, что в процедурах ПередЗаписью, ПриЗаписи должны стоять отсечки, как в типовых конфигурациях, иначе некоторые объекты может не получиться записать.

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

    Reply
  26. json

    (25)

    объясню, что значит уровень = 4.

    Вот ты выгружаешь элемент номенклатуры — сам элемент со всеми реквизитами это уровень 0

    Все ссылки, которые в нем упоминаются — это уровень 1.

    Все ссылки, которые упоминаются в уровне 1 — это уровень 2

    и т.д.

    Вот выгружаешь ты элемент номенклатуры, который лежит в папке, которая вложена в другую папку, которая вложена в другую папку и т.д.

    При уровне = 4 у тебя выгружается сам выгружаемый элемент номенклатуры, а также четыре уровня папок в которые он вложен.

    Если уровень вложенности у переносимого элемента больше 4, четвертый родитель вверх от элемента будет помещен в корень.

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

    Чтобы этого избежать я написал в предыдущем сообщении что нужно сделать. А именно необходимо перенести отдельно всю иерархию папок.

    А потом уже переносить элементы с настройкой уровень = 0 или 1 в зависимости от того хотим ли мы создавать ссылки, которые вложены в переносимый элемент или нет

    Reply
  27. jack19
    В перегружаемом вами справочнике номенклатуры, похоже, что больше 4 уровней, поэтому часть папок оказалась в корне, потому что не переносился их родитель.

    Дело в том, что не часть папок оказалось в корне, а все папки и элементы оказались в корне. Но в общем понял, попробую в два этапа переносить. Не понял только про «отсечки», что это. Что касается указанных мною сообщений об ошибках, то понял, что дело в измененной конфигурации базы-источника (добавлены реквизиты), сделал аналогичные изменения в базе-приемника, посмотрим. Спасибо за то, что помогаете разобраться.

    Reply
  28. json

    (28) прикладываю пример на скриншоте, как выглядит код типовых конфигурациях.

    Вот в примере, если запись происходит в особом режиме, то запись выполняем без проверок (Возврат — означает что ничего не проверяем и не дозаполняем, а просто выходим из процедуры и записываем, а в обычном режиме на примере выполняются проверки и где-то при проверках может установиться Отказ=Истина или выскочить исключение)

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

     Если ОбменДанными.Загрузка  Тогда
    Возврат;
    КонецЕсли;
    Reply
  29. jack19

    Да, спасибо. Еще одно уточнение. Как я понял «уровень» касается вложенных реквизитов, а не родителей? Т.е., если я буду переносить элементы с «уровень = 0», они попадут в ранее перенесенные папки? А что значит «уровень = 1»? Что будут создаваться элементы тех справочников, на которые ссылаются элементы номенклатуры?

    Reply
  30. json

    (30)

    Родитель — это тоже реквизит справочника номенклатуры. Если переносим с уровень=0 и родителя нет в базе, то загружаемый элемент попадет в корень. Если родитель на момент загрузки есть в базе, то загружаемый элемент будет подчиненным этому родителю.

    Уровень = 1 — значит, что все ссылочные объекты, которые есть в реквизитах переносимого элемента, тоже создадутся и заполнятся как в базе источнике.

    Уровень = 0 — значит, что все ссылочные объекты, которые есть в реквизитах — не будут создаваться, а если объект, на который ссылается реквизит в базе отсутствует, то в реквизите будет битая ссылка.

    Reply
  31. jack19

    (31) Сделал, как вы сказали — в 2 этапа. Сначала поставил Отбор: ЭтоГруппа = Истина. Перенеслись все папки. Потом поставил ЭтоГруппа = Ложь. Перенеслись все элементы, но не в папки, а в корень. Кроме того в поле «Код» элементов записывается не код, а часть наименования.

    Reply

Leave a Comment

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