Сравнение элементов справочников двух баз по COM соединению. Теперь на управляемых формах.




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

37 Comments

  1. OBEH

    «Идентичность» — Это код и наименование в справочниках разных конф?

    Reply ↓
  2. ediks

    (1)Я вроде написал, что сравнение идет по выбранным реквизитам. Кнопка «Настройка» открывает форму выбора реквизитов.

    Reply ↓
  3. OBEH

    (2)Я еще не смотрел обработку. Просто вопрос возник.

    Если, например, по коду можно, то дело стоящее.

    А если, например, одна база на платформе 8.1, а другая база на платформе 8.2?

    И еще. Идея. Мне кажется, коль пошла такая пьянка, может быть стоит добавить

    на форму кнопку, которая формирует отчет по несовпадающим элементам?

    Reply ↓
  4. ediks

    (3)

    1) Сравнение идет в том числе и по коду, если он используется у справочника и если он выбран для сравнения.

    2) Добавить поддержку 8.2 достаточно легко — в списке выбора нужно добавить 8.2 так, как это сделано для 8.1. Но основное предназначение обработки — сравнение элементов справочников для баз, являющихся родственными. Например, в новую базу переносили данные и нужно проверить несовпадающие элементы.

    3) В табличную часть выводятся только несовпадения. Во встроенной справке это описано. Если в контестном меню выбрать пункт «Вывести список», то получим отчет по несовпадениям. В моем случае я выводил коды несовпадающих элементов и перегружал их из исходной базы.

    Reply ↓
  5. OBEH

    (4)Ну, так скоро и будет, переходы на «родственные» базы, но 8.1 на 8.2

    Я так думаю, к новому году все потянутся на 8.2

    Reply ↓
  6. OBEH

    Поверил человеку. А тут, оказывается, с 8.2 не работает

    Reply ↓
  7. OBEH

    Переконвертировал под 8.2. Вроде, заработало.

    Вот только шаг счетчика, наверное, надо бы на больших объемах ставить, например, 100

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

    Reply ↓
  8. OBEH

    Во! Рухнула задача по ошибке «Недостаточно памяти».

    Примерно, на 40 тысяче записи.

    И чего делать?

    Reply ↓
  9. ediks

    (6) Насчет 8.2: я написал что нужно сделать на форме — добавить в список выбора строку 8.2. И ничего не нужно конвертировать — из под 8.1 прекрасно соединяется с 8.2.

    (7) Процесс вывода занимает сравнительно небольшое время по сравнению с поиском COM объектов. Но код открыт — все в твоих руках.

    (8) Попробуй уменьшить число сравниваемых реквизитов или задать фильтры в отборе. По умолчанию, сравнивается по всем реквизитам. Соответственно, если справочник содержит много реквизитов, то выборка получается очень большая. На 8 гигах оперативки при минимальном количестве сравниваемых реквизитов (1 — 😀 ) удавалось сравнить справочник договоров в 500-600 тыс. записей.

    Reply ↓
  10. OBEH

    (9)У меня не получилось установить выбор на 8.2

    Там показывалось только 8.1(по умолчанию) и 8.0

    8.2, вообще, даже вручную, не проходит. Тем более «прекрасно» не соединяется с 8.2

    Процесс вывода можешь сам проверить, установив шаг, например, сто.

    Будешь удивлен.

    Количество сравниваемых реквизитов я сделал два — Код и Наименование.

    Меньше нет смысла, а больше не надо.

    По поводу того, что все в моих руках — конечно, могу код править,

    но, пока не вижу смысла большого

    Reply ↓
  11. ediks

    (10) Добавлять в список выбора 8.2 нужно в режиме конфигуратора.

    Reply ↓
  12. Virsy

    Выпала ошибка при сравнении реквизитов табличной части справочника:

    {ВнешняяОбработка.СравнениеСправочниковВРазныхБазах(32)}: Ошибка при установке значения атрибута контекста (Текст): {(41, 14)}: Поле не найдено «Справочники.НомерСтроки»

    Справочники.<<?>>НомерСтроки,

    Построитель.Текст = СформироватьТекстЗапроса(1, КоличествоВВыборке);

    по причине:

    {(41, 14)}: Поле не найдено «Справочники.НомерСтроки»

    Справочники.<<?>>НомерСтроки,

    Reply ↓
  13. ediks

    (12) Табличные части не сравниваются. Это описано во встроенной справке.

    Reply ↓
  14. Virsy

    (13) тогда неплохо бы их не добавлять в список реквизитов

    Reply ↓
  15. ediks

    (14) Был такой глюк 😀 . Исправился. Спасибо за наводку.

    Reply ↓
  16. Tokiy

    Ешчо один «глюк»: когда есть две табличные части, например «Образование» и «ДопОбразование», то в этом куске

    //

    // выкусим наименования табличных частей

    Для каждого ТабличнаяЧасть Из Метаданные.Справочники[ТипОбъекта].ТабличныеЧасти Цикл

    ТекстЗапроса = СтрЗаменить(ТекстЗапроса, ТабличнаяЧасть.Имя + «.», «»);

    КонецЦикла;


    //

    в слове «ДопОбразование.» он обрезает «Образование.» и потом ругается на слово «Доп»:

    {(25, 14)}: Поле не найдено «Справочники.Доп»

    Справочники.<<?>>Доп

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

    Reply ↓
  17. Tokiy

    А вообще качал в надежде что обрабатывает табличные части..

    Reply ↓
  18. v_id

    {ВнешняяОбработка.СравнениеСправочниковВРазныхБазах(583)}: Метод объекта не обнаружен (СообщитьИнформациюПользователю)

    ОбщегоНазначения.СообщитьИнформациюПользователю(«Количество записей внешней базы: » + Количество);

    8.1 Две УТ 10.3

    Reply ↓
  19. ediks

    (16) Спасибо за наводку. Вроде исправил.

    Насчет корректности подхода: простой перебор реквизитов по метаданным не дает таких реквизитов, как Владелец, Родитель, Код, Наименование, Пометка удаления, Предопределенный и т.д. Соответственно, их наличие необходимо анализировать дополнительно. Мне показалось это несколько неудобным.

    Конструктор запроса делает этот анализ за меня, корректно выбирая все реквизиты.

    Reply ↓
  20. ediks

    (18) Исправил этот глюк. Действительно, в УТ нет такой процедуры. Спасибо, что подсказали.

    Reply ↓
  21. v_id

    Я конечно придираюсь, но «1» и «-1» не очень наглядно, может использовать раскраску типа зеленый есть в текущей нет в копии, красный наоборот?

    Reply ↓
  22. ediks

    (21) Добавил «цветовую дифференциацию штанов строк» 😀 , попросту говоря, раскраску. А вообще я сразу делаю отбор по реквизиту «Свертка», вывожу список и копирую отобранные коды в какую-нибудь обработку обмена.

    Reply ↓
  23. powerpc

    А для сравнения документов можно сделать такую же обработку ? Особенно необходимо для сравнения выгрузки УТ в Бухгалтерию. Спасибо заранее

    Reply ↓
  24. pantera190

    спасибо.пригодилось.

    Reply ↓
  25. slav4ek

    спасибо)

    Reply ↓
  26. bar_s

    спасибо.пригодилось.

    Reply ↓
  27. RTA

    кто может дать на нее прямую ссылку?

    Reply ↓
  28. rasswet

    обычная форма. где там закладка «расхождения»? или таковой нет?

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

    Reply ↓
  29. ediks

    (28)

    1) В обычной форме нет закладки «Расхождения» — данные выводятся в табличной части ниже, под отбором.

    2) Каждая строка идентифицируется полем ГУИД и свертка. -1 означает, что объект находится во внешней базе. Соответственно, у Вас получается, что элементы справочника различаются, например, по полю ГУИД, если у них одинаковые код и наименование. Это возможно, например, если в базах элементы заводились вручную.

    В управляемой форме то же самое, но нет раскраски строк.

    Reply ↓
  30. lees

    Спасибо! В хозяйстве пригодилось. Еще б была возможность сравнивать 8 с 7.7, тогда было б ваще круть.

    Reply ↓
  31. www2007

    Спасибо, пригодилось, и времени сэкономило немало… Полезная вещь..

    Reply ↓
  32. SkorikA

    Ошибочка:

    {ВнешняяОбработка.СравнениеСправочниковВРазныхБазах.МодульОбъекта(60)}: Ошибка при вызове метода контекста (Добавить)

    Построитель.Отбор.Добавить(«Владелец»,, «Владелец»);

    по причине:

    Недопустимое значение параметра (параметр номер ‘1’)

    Я так понимаю, если справочник подчинен владельцу и его не выбрал — то не работает?

    Reply ↓
  33. ediks

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

    Reply ↓
  34. Светлый ум

    {ВнешняяОбработка.СравнениеСправочниковВРазныхБазах.МодульОбъекта(577)}: Ошибка при установке значения атрибута контекста (Текст)

    КомПостроитель.Текст = СформироватьТекстЗапроса(-1, КоличествоВВыборке);

    по причине:

    Произошла исключительная ситуация (1C:Enterprise 8.2.19.121): {(18, 14)}: Поле не найдено «Справочники.ФактическийАдресУдалить»

    Справочники.<<?>>ФактическийАдресУдалить,

    Reply ↓
  35. Светлый ум

    (35) Светлый ум,

    Ошибка возникала при работе с Розница 1.0 —> БП 2.0

    (сравнение справочника Контрагенты)

    Поправил текст процедуры и заработало:

    Функция СформироватьТекстЗапроса(пСвертка = 1, пКоличествоВВыборке = 0)
    
    Если НЕ ЗначениеЗаполнено(ТипОбъекта) Тогда Возврат «» КонецЕсли;
    
    УсловиеПоКоличеству = ?(пКоличествоВВыборке = 0, «», «ПЕРВЫЕ » + Формат(пКоличествоВВыборке, «ЧГ=0»));
    ТекстЗапроса =
    «ВЫБРАТЬ » + УсловиеПоКоличеству + »
    | » + пСвертка + » КАК Свертка,
    | Выразить(«»»» КАК Строка(36)) КАК ГУИД, Справочники.Ссылка КАК Ссылка, «;
    Для каждого СтрокаТЧ Из РеквизитыОбъекта Цикл
    
    Если Не Прав(СтрокаТЧ.Имя,7)= «Удалить»
    И Не СтрокаТЧ.Имя = «БанковскиеРеквизиты»
    И Не СтрокаТЧ.Имя = «ГруппаПолучателейСкидки»
    И Не СтрокаТЧ.Имя = «ПодписьДолжность»
    И Не СтрокаТЧ.Имя = «ПодписьФИО»
    И Не СтрокаТЧ.Имя = «ГоловнаяОрганизация»  Тогда
    
    Если СтрокаТЧ.Пометка Тогда
    ТекстЗапроса = ТекстЗапроса + »
    | Справочники.» + СтрокаТЧ.Имя + «,»;
    КонецЕсли;
    КонецЕсли;
    
    КонецЦикла;
    ТекстЗапроса = ЛЕВ(ТекстЗапроса, СтрДлина(ТекстЗапроса) — 1) + »
    |ИЗ
    | Справочник.» + ТипОбъекта + » КАК Справочники»;
    
    Возврат ТекстЗапроса;
    
    КонецФункции // СформироватьТекстЗапроса()
    

    Показать

    Reply ↓
  36. Yran

    Скачал, попробовал запустить на БП 3.0 — выдала ошибку: Основная таблица динамического списка задана не верно. Не задан ни текст запроса, ни основная таблица динамического списка.

    При открытии зачем спрашивает?

    Reply ↓
  37. ediks

    (37) Исправил, спасибо за информацию. Попробуйте скачать еще раз. Если возникнут проблемы, пишите, вышлю в личку.

    Reply ↓

Leave a Comment

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