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




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

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

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

<?php // Полная загрузка сервисных книжек, создан 2026-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 не будет опубликован. Обязательные поля помечены *