Поиск ссылок прямыми запросами для SQL версии.




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

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

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

<?php // Полная загрузка сервисных книжек, создан 2024-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='\

24 Comments

  1. Утюг

    глРаспечататьТаблицу<<?>>(тзСсылок);

    {C:TEMPПОИСКССЫЛОК.ERT(361)}: Процедура не обнаружена (глРаспечататьТаблицу)

    Reply
  2. Boog

    глРаспечататьТаблицу<<?>>(тзСсылок);

    {E:!1C_BASEEXTFORMS1POISKSSYLOK.ERT(361)}: Процедура не обнаружена (глРаспечататьТаблицу)

    Reply
  3. Boog

    Извиняюсь за дубль — не обновил страницу.

    Reply
  4. Boog

    Ошибка обработки запроса:

    SELECT DISTINCT

    :Объект As [Объект $Справочник.Контрагенты]

    ,Tab.DOCID As [Ссылка $Документ]

    ,’Субконто’ As Реквизит

    ,’Проводка’ As Метаданные

    ,J.IDDOCDEF AS Ссылка_вид

    , CASE :Объект

    When

    Tab.DTSC0 then ‘Дт1’

    When

    Tab.KTSC0 then ‘Кт1’

    When

    Tab.DTSC1 then ‘Дт1’

    When

    Tab.KTSC1 then ‘Кт1’

    When

    Tab.DTSC2 then ‘Дт2’

    When

    Tab.KTSC2 then ‘Кт2’

    When

    Tab.DTSC3 then ‘Дт3’

    When

    Tab.KTSC3 then ‘Кт3’

    END As Субконто

    FROM

    _1SENTRY As Tab

    INNER JOIN _1sjourn J On J.IDDOC = Tab.DOCID

    where (Tab.DTSC0 = :Объект And Tab.VDTSC0 in (450)) OR (Tab.KTSC0 = :Объект And Tab.VKTSC0 in (450))

    OR (Tab.DTSC1 = :Объект And Tab.VDTSC1 in (450)) OR (Tab.KTSC1 = :Объект And Tab.VKTSC1 in (450))

    OR (Tab.DTSC2 = :Объект And Tab.VDTSC2 in (450)) OR (Tab.KTSC2 = :Объект And Tab.VKTSC2 in (450))

    OR (Tab.DTSC3 = :Объект And Tab.VDTSC3 in (450)) OR (Tab.KTSC3 = :Объект And Tab.VKTSC3 in (450))

    State 42S22, native 207, message [Microsoft][ODBC SQL Server Driver][SQL Server]Invalid column name ‘DTSC3’.

    Reply
  5. MadDAD

    (1), (2). Прошу прощения не отвязал от конфгурации.

    (3) — Максимально количество субконто не определялось, стояло 4 по умолчанию.

    Добавил недостающую процедуру вывода таблицы значений — удобно, если не хочется делать печатную формую

    Поправил определение количества субконто.

    Reply
  6. Boog

    В процедуре НайтиРеквизитыСсылок в цикле по субконто, нужно делать цикл от 1 по КолвоСубконто-1

    и в ДопКолонке писать

    When

    Tab.DTSC»+Субконто+» then ‘Дт»+(Субконто+1)+»‘

    When

    Tab.KTSC»+Субконто+» then ‘Кт»+(Субконто+1)+»‘»;

    Тогда взлетит.

    Reply
  7. MadDAD

    (6) Точно 🙂 Затупил… Хотя у меня работает с таким запросом.

    Reply
  8. Boog

    А что в колонке Ссылка_вид?

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

    Reply
  9. Утюг

    1. Еще бы надо проверять наличие таблицы _1sentry и, в зависимости от ее наличия, обрабатывать или пропускать строки 317-340. В ТиС и ЗиК ее нет и получаем ошибку.

    2. В _1sentry нумерация субконто начинается с ноля, т.е. при 3-х субконто поля будут называться DTSC0, DTSC1, DTSC2. А в обработке они начинаются с единицы, в связи с чем имеем ошибку — Недопустимое имя столбца «DTSC3». Соответственно, строку 330 надо изменить на

    Для Субконто = 0 По КолвоСубконто-1 Цикл
    Reply
  10. Утюг

    О, пока разглядывал, тут уже написали. Но Boog (6) не до конца прав — см. мое (9), п.2.

    Reply
  11. MadDAD

    (10) В принципе то же самое, только у меня первые субконто задаются до цикла по остальным, тогда цикл с 1.

    Добавил определение наличия таблицы, поправил максимально количество субконто, (6)поправил нумерацию субконто.

    (8) Ссылка_вид содержит идентификатор вида документа для типизации документа в запросе. Если идет запрос по регистрам — в качестве сылки возвращается документ, который делал движения. В остальных случаях если ссылка не документ, то и вид документа не нужен, соответственно он пустой.

    Это служебное поле, которое в принципе можно прятать.

    Reply
  12. Boog

    С проверкой на ведение бух. учета в базе вообще — согласен.

    По поводу цикла от 0 по КолвоСубконто-1:

    там перед циклом определяются DTSC0 и KTSC0. Отдельно. (и условия для них).

    Reply
  13. Утюг

    (11) впопыхах не заметил

    (12) угу, теперь летает

    Reply
  14. jj_mail

    Ошибка обработки запроса:

    SELECT DISTINCT

    :Объект As [Объект $Справочник.Фирмы]

    ,Tab.ID As [Ссылка $Справочник.Фирмы]

    ,’Родитель’ As Реквизит

    ,’Справочник.Фирмы’ As Метаданные

    FROM

    $Справочник.Фирмы As Tab

    where Tab.PARENTID = :Объект

    State 42S22, native 207, message [Microsoft][ODBC SQL Server Driver][SQL Server]Invalid column name ‘PARENTID’.

    —————————

    Ошибка обработки запроса:

    SELECT DISTINCT

    :Объект As [Объект $Справочник.Фирмы]

    ,Tab.DOCID As [Ссылка $Документ]

    ,’Субконто’ As Реквизит

    ,’Проводка’ As Метаданные

    ,J.IDDOCDEF AS Ссылка_вид

    , CASE :Объект

    When

    Tab.DTSC0 then ‘Дт1’

    When

    Tab.KTSC0 then ‘Кт1’

    When

    Tab.DTSC1 then ‘Дт2’

    When

    Tab.KTSC1 then ‘Кт2’

    When

    Tab.DTSC2 then ‘Дт3’

    When

    Tab.KTSC2 then ‘Кт3’

    END As Субконто

    FROM

    _1SENTRY As Tab

    INNER JOIN _1sjourn J On J.IDDOC = Tab.DOCID

    where (Tab.DTSC0 = :Объект And Tab.VDTSC0 in ()) OR (Tab.KTSC0 = :Объект And Tab.VKTSC0 in ())

    OR (Tab.DTSC1 = :Объект And Tab.VDTSC1 in ()) OR (Tab.KTSC1 = :Объект And Tab.VKTSC1 in ())

    OR (Tab.DTSC2 = :Объект And Tab.VDTSC2 in ()) OR (Tab.KTSC2 = :Объект And Tab.VKTSC2 in ())

    State 42000, native 102, message [Microsoft][ODBC SQL Server Driver][SQL Server]Incorrect syntax near ‘)’.

    Reply
  15. MadDAD

    (14)

    Не был предусмотрен вариант справочников с одним уровнем.

    Не был предусмотрен вариант остутствия объекта в видах субконто.

    Reply
  16. jj_mail

    Заголовок = глСтрРасставитьПробелы<<?>>(тз.ИмяКолонки(НомерКолонки));

    {D:ПОИСКССЫЛОК.ERT(33)}: Функция не обнаружена (глСтрРасставитьПробелы)

    Reply
  17. MadDAD

    (16) Извиняюсь, выгрузил старую версию. Сейчас уже лежит новая.

    Reply
  18. afanasko

    18. Очень интересно сделано. Код понравился ))) +

    Reply
  19. maxim_ro

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

    Reply
  20. MadDAD

    Новая версия. Теперь учтены реквизиты неопределенного типа (Спасибо maxim_ro).

    Reply
  21. MadDAD

    (19) Спасибо за указанный недочет. Для собственного пользования обработка давно уже доработана, здесь не обновил.

    Самое большое время тратится на запрос по таблице проводок. Если эта таблица не интересует — можно смело закомментировать. Вообще обработка и создавалась для того чтобы можно было искать ссылки выборочно а не по всем метаданным.

    Reply
  22. s_s_v

    Для реквизитов неопределенного типа в отчете для документа стоит

    сзНеопределенных.Установить(«Документ», «‘A1’+»);

    т.е. в запросах получается типа того

    where Tab.sp2265 = ‘A1’+’ 7K’+’ 65GAE ‘

    Провел трассировку стандартного метода НайтиСсылки() и там во временную табл. для документа передается ‘O1’, т.е. строка должна получиться вида

    where Tab.sp2265 = ‘О1’+’ 7K’+’ 65GAE ‘

    Может подскажете как всетаки корректно определить правильное значение для документа или справочника.

    Reply
  23. MadDAD

    (22) проверил сейчас. Действительно — верно ‘О1’. Видимо при разработке недостаточно тестировал.

    Версия обновлена.

    Reply
  24. s_s_v

    Кстати я для справочника не проверял.

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

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

    Но если добавить фильтры по времени, то на больших базах для документов поиск можно сократить в разы

    Reply

Leave a Comment

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