7.7 Кто изменил документ????




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

57 Comments

  1. NoMax

    (0) Очень хорошее решение.

    Протестировал на своих журналах:

    407 Мб — 185 сек.

    746 Мб — 315 сек.

    и главное не подвешивается сама 1С.

    Reply
  2. Noy

    гм… нашел «ошибку» — текстовый реквизит был длиной всего 10 символов (стандарт так сказать) — исправил

    Немного переделал алгоритм и отключил видимость ТЗ при выводе данных — получился очень серьезный выигрыш в скорости на «объемных» запросах.

    NoMax — попробуй плз еще раз на своих журналах и выложи результаты

    Reply
  3. JohnyDeath

    Смотрим http://www.1cpp.ru/docum/icpp/html/LogDataProvider.html

    Где-то даже есть готовая обработка с ТП+Поставщик_ЖурналРегистрации

    Reply
  4. Noy

    (3) Про это я в курсе, да вот все руки никак не дойдут разобраться с ТП

    В защиту своей обработки могу сказать только: «не требует внешних компонент».

    Reply
  5. Ёпрст

    (3) в репозитарии валяется класс, в конфе со всеми классами, если не ошибаюсь

    🙂

    Reply
  6. JohnyDeath

    (5) Да, там она точно есть, но я не хотел пугать людей такими страшными словами ;)))

    Reply
  7. rimma_n

    А почему при первом запуске пишет — демонстрационный пример, кол-во запусков ограничено?

    Reply
  8. Noy

    (7) Там «безлимитная лицензия» для программистов 1С 🙂

    Reply
  9. Noy

    (3,5) немного разобрался — с LogProvider из 1Cpp — вещь конечно очень рульная! Кодить практически не надо — все очень просто.

    При сканировании всего журнала скорость такая же как в моей обработке, а при сканировании только последних записей — 1Cpp быстрее в 2-3 раза.

    Так что кому не хватает скорости — используйте 1Cpp.

    Reply
  10. Just

    А у меня не работает, поиск по документу вроде бы идет, а результат пустой, хотя в журнале записи есть, Бухгалтерия 77

    Reply
  11. ЕЮИ

    у меня тоже ничего не показывает,Бухгалтерия 7.7

    Reply
  12. Noy

    (10,11) Поиск по тексту работает? Например по строке «OpenSession» ?

    Reply
  13. ЕЮИ

    Да,работает

    Reply
  14. ЕЮИ

    (11,12) Да,работает

    Reply
  15. Noy

    Хм… а в верхнем поле там где «Значение для поиска» что-то выбрано?

    и еще вопрос: _стандартный_ фильтр по Журналу регистрации что-то показывает? может кто-то просто журнал потер?

    Reply
  16. ЕЮИ

    (15) когда выбираю документ,из журнала,идет поиск,но по окончании журнал событий пуст.

    Сам список фильтра пустой,но если делаю отбор,все формируется.

    Reply
  17. Noy

    То есть если вызвать обработку, находясь в журнале, то в значение фильтра не попадает текущий документ? я правильно понял?

    Если так, то либо у вас не установлена ВК FormEx (без нее это не работает), либо вы вызываете обработку через «дополнительные возможности» (или другим похожим способом).

    Для того, что бы обработка «подхватывала» документ из журнала ее нужно вызывать с помощью «хоткея» или с помощью меню Файл-Открыть или же с помощью обработки «Быстрый доступ к отчетам» (ссылка есть в описании).

    Reply
  18. rasswet

    (8) это хорошо! 😉

    Reply
  19. Shaman100M

    Зачем просматривать весь журнал регистрации, если документ был создан недавно? Вот хорошая идея: можно предварительно пробежать ЖР один раз и сделать что-то вроде индекса, самое простое:

    Дата —> Количество байтов с начала ЖР для метода Skip().

    Т.образом для поиска всех записей по выбранному документу глубину поиска ЖР указывать не надо. Берем дату документа (предполагая, что он был создан этой датой) и получаем кол-во байтов для Skip(), ну а дальше по схеме. Можно сделать сложнее и точнее, для любых объектов: Диапазон внутренних ID объекта определенного типа —> кол-во байт с начала ЖР, указывающих на начало диапазона.

    Reply
  20. Just

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

    Reply
  21. MadDAD

    (0) Однозначно полезная штука, единственный минус — пришлось дорабатывать для распределенки:

    //Код:

    Функция ПолучитьИД(Объект)

    ОбъектСтр=ЗначениеВСтрокуВнутр(Объект);

    СП=СоздатьОбъект(«СписокЗначений»);

    СП.ИзСтрокиСРазделителями(Сред(ОбъектСтр,2,СтрДлина(ОбъектСтр)-2));

    КодОбъекта=СоздатьОбъект(«Текст»);

    КодОбъекта.ДобавитьСтроку(СтрЗаменить(СП.ПолучитьЗначение(СП.РазмерСписка()),» «,РазделительСтрок));

    ОбъектСтр=СП.ПолучитьЗначение(1);

    Для х=1 по КодОбъекта.КоличествоСтрок() Цикл

    Если ПустоеЗначение(КодОбъекта.ПолучитьСтроку(х))=1 тогда Продолжить; КонецЕсли;

    ОбъектСтр=ОбъектСтр+»/»+?(СтрЧислоВхождений(КодОбъекта.ПолучитьСтроку(х), СокрЛП(ИбСозданияОбъекта(Объект)))<>0, Шаблон(«([ИбСозданияОбъекта(Объект)])[СтрЗаменить(КодОбъекта.ПолучитьСтроку(х),ИбСозданияОбъекта(Объект),»»»»)]») ,КодОбъекта.ПолучитьСтроку(х));

    //КодОбъекта.ПолучитьСтроку(х);

    КонецЦикла;

    Возврат ОбъектСтр;

    КонецФункции

    ///Код

    Reply
  22. MadDAD

    (10,11,15,20)

    Строка поиска объекта в распределенке выглядит:

    «;O/41516/(CEN)881174;»

    Для не распределенки:

    «;O/41516/881174CEN;»

    Скорее всего из-за этого и проблема.

    См. (21) с этим и была связана доработка.

    Reply
  23. Noy

    (19) Шаман, спасибо за замечания, но попробую «отбиться»:

    Обработка и не просматривает журнал весь регистрации. Как раз Skip() и используется — просто «скипается» сразу не все, а по одному мегабайту — опытным вычислил что быстрее 200 раз скипнуть по метру, чем один раз 200 метров.

    Построение индекса к журналу — вообще не нужно, лучше сразу весь журнал перегнать в табличку на SQL (хотя бы экспрес) и периодически обновлять — но это уже совершенно другая обработка…

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

    И последнее — привязвываться к дате документа считаю не правильным — документ мог быть создан горздо раньше! Вот если бы был метод считывать файл с конца — то было бы вообще супер — ищем до события «Создан» и вуаля! но я такого метода не знаю 🙁

    Reply
  24. Noy

    (21,22) Да, так и есть — обработка не работает в распределенных базах (по причине отсутствия у меня оных) о чем я и забыл указать в описании…

    Спасибо за поправку.

    (20) Возможно у тебя распределенка? попробуй исправить обработку как указал MadDAD в (21).

    Reply
  25. Just

    ну да, у меня УРБД, поправлю, как нужда заставит

    Reply
  26. rimma_n

    Хорошо бы добавить кнопочку Печать!!!

    если много записей, то печать экрана неудобна

    Reply
  27. Noy

    (26,27,28,29,30) Учитывая настойчивость предложения — постараюсь сегодня доделать 😉

    Reply
  28. Shaman100M

    Про «быстрее скипнуть по метру» не знал. А на сколько быстрее?

    Индекс по датам получается небольшой, — при возрасте базы в 5 лет: 1800 значений, при открытии можно восстанавливать в какую-нить переменную. Про период, да, возможно его вводить пользователю по времени дольше, чем будет осуществляться сам поиск, но откуда ты знаешь, что у него в голове (это как анек про Винни с пятачком) и что для него удобнее? 😉 Период, он есть почти в любом отчете, и его выбор воспринимается как полезная привычка. В довесок — не всегда нужен поиск до конца ЖР, указание периода сократит время поиска.

    Ну, а для документа, период можно не выбирать, — исходя из предположения, что документ все же был создан датой документа (так и будет в 90-95% случаев), ищем до туда, показываем таблицу, а там, если не встретили «Создан», — предупреждаем, что можно поискать в боле раннем периоде. Ну это так, как бы я сделал. Однако, обошелся просто статейкой про Skip. http://infostart.ru/blogs/261/

    Метода считывать файл с конца нет — вот и приходится использовать его… Или, если гора не идет к Магомету… переписать ЖР наоборот, и читать его сначала. :)))))

    Reply
  29. MadDAD

    Кстати может будет полезно, посмотри у меня в разработках — «отчет по обменам УРБД». Там тоже парсится mlg, но только при помощи утилиты Find из ДОСовского набора.

    Reply
  30. Noy

    (33) Я пробовал Find, как один из вариантов — скорость поиска такая же самая — поэтому решил остановиться на таком варианте.

    Reply
  31. Noy

    (32) На счет «быстрее» я погорячился — только что потестировал чуть-чуть: разница в пределах погрешности (на 15 тестах по пропуску 220 Мб разлет был 13,5 сек до 16,9 не зависимо от шага) — в любом случае пошагово «не торомозит» и позволяет выводить строку состояния 🙂

    Reply
  32. Noy

    Добавил печать, исправил работу в распределенных базах.

    Reply
  33. itmind

    Насколько быстро работает это обработка на журналах под 2 Гб? (притом каждый месяц заводится новый а старые сохраняются)

    Reply
  34. Gal4onok

    а для 8.1 такого же нет??

    Reply
  35. Noy

    (37) Вам, батенька, давно пора от этих журналов отказываться…

    А вообще скорость такая же, как у стандартной…

    Reply
  36. Dolly_EV

    Автору — баальшой респект! Внедрил за 10 минут!

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

    Кстати, если обработку встраивают в конфу, то ХотКей() cтоит написать так:

    и в «ПриОткрытии()» поправить..

    РасположениеФайла(Катал,Файл);
    Если ПустоеЗначение(Катал)+ПустоеЗначение(Файл)=2 Тогда
    //Внутри
    ОткрФорму = «ОткрытьФорму(«»Обработка.ОтборСобытийПоОбъектуИзMLG»+»»»,,»»»»);»;
    Иначе
    //Вовне
    ОткрФорму = «ОткрытьФорму(«»Отчет#»+_GetPerformanceCounter()+»»»,,»»»+Катал+Файл+»»»);»;
    КонецЕсли;
    Сервис.ДобавитьГлобальныйМодуль(»
    |Перем fn_mlgReader Экспорт;
    |
    |Процедура ПриНажатииКнопкиКлавиатуры(Конт,КодКлавиши,Alt,Shift,Ctrl,Символ,ФСО)
    |Если КодКлавиши<>192 Тогда Возврат; КонецЕсли;
    |Если Shift+Ctrl<>2 Тогда Возврат; КонецЕсли;»+РазделительСтрок+
    ОткрФорму+»
    |КонецПроцедуры
    

    Показать

    Reply
  37. Dolly_EV

    И еще косячок в «Печать()»

    заг=?(ТипОб=1,»элементу справочника «»»+спр.Вид()+»»»: «+спр,»документу: «+спр);

    не спр2 !!

    Reply
  38. Noy

    (40) Если встраивать в конфу, то хоткей из обработки лучше вообще удалить, а пользоваться стандартным хоткеем, который вешается на пункты меню или панели…

    Спасибо за отзыв ;).

    Reply
  39. Noy

    (41) Сенкс, подправлю!

    Reply
  40. ЕЮИ

    (22) Да уменя УРБД,заново все установили.

    Что было загружено от пользователя,при поиске просто формируется строка в Комментариях

    «2; Новый; O/13053/(СГЕ)3006; Авансовый отчет Б0000001 14.01.2009 10:00:10;»

    т.е. без расшифровки пользователя и всего остального,так и должно быть.

    Было бы здорово делать поиск по измененям не одного документа,а конкретно по пользователю,особенно когда работаешь в УРБД,загрузил файлик,и сразу посмотрел,что было изменно.

    Reply
  41. Noy

    (44) Можно искать по пользователю — пишешь в строку поиска ;ИмяПользователяКакОнЗаданВКонфигураторе;(например ;Света;) и обработка находит все записи по пользователю. Единственное неудобство: моя обработка распознает события только по изменению справочников и документов, остальные события идут как комментарий.

    Reply
  42. ЕЮИ

    (44) Сейчас попробую.

    Reply
  43. ЕЮИ

    (44) Сегодня загрузили файл,при формировании отчета,не выдает никаких данных за 2009 год,пользователем было сделано много документов,а при поиске дала только информацию по 20 документам,и то за 2008 год.

    Reply
  44. Noy

    (47) давай в личку

    Reply
  45. ЕЮИ

    (47) Простите,а что это?

    Я бухгалтер,но с 1С работаю с 1998.

    Reply
  46. Noy

    У вас на странице справа в верхнем углу должен появиться блок с сообщением от меня — нажмите ответить — откроется окно по типу «аськи» (чата) — там мы сможем пообщаться

    Reply
  47. phsin

    Добрый день, написать сообщение лично почему-то не получается, поэтому пишу здесь.

    Посмотрел вашу обработку , очень понравилось, появились идеи как ее можно можно еще улучшишь (на мой взгляд ;),

    результатом изысканий появилась такая вот статья http://infostart.ru/public/63659/

    Если вы не против — опубликую, если против — уберу…

    С уважением.

    Reply
  48. brr

    Автор не пробовал использовать для парсинга журнала текстдрайвер?

    Reply
  49. Noy

    (52) Можно чуть подробнее — что за «текстдрайвер»?

    или хотя бы ссылочку

    Reply
  50. brr

    Погугли: Text ODBC Driver

    Reply
  51. Noy

    (54) Судя по всему через Text ODBC Driver будет быстрее, да и условия можно сразу накладывать. Большое спасибо за подсказку, но обработка на данный момент не актуальна — переписывать не буду.

    Reply
  52. Ray_uk

    спасибо за обработку. Спасла ))

    Reply
  53. evgenum

    Добавь в обработку красивый вывод сообщений УРИБ.

    В функции ПеревестиВТЗ заменить:

    Если СтрЧислоВхождений(«Docs,Refs»,Тек2.ПолучитьСтроку(5))=0 тогда

    на

    Если СтрЧислоВхождений(«Docs,Refs,Distr»,Тек2.ПолучитьСтроку(5))=0 тогда

    В функции ПредставлениеСобытия добавить:

    ИначеЕсли Тип=»Distr» Тогда

    Если Событие=»DistUplStatus» Тогда

    Возврат «Загрузка изменений данных»;

    КонецЕсли;

    Возврат «Обмен: «+Событие;

    Reply
  54. kleo19866

    РасположениеФайла(Катал,Файл);

    Если ПустоеЗначение(Катал)+ПустоеЗначение(Файл)=2 Тогда

    //Внутри

    ОткрФорму = «ОткрытьФорму(«»Обработка.ОтборСобытийПоОбъектуИзMLG»+»»»,,»»»»);»;

    Иначе

    //Вовне

    ОткрФорму = «ОткрытьФорму(«»Отчет#»+_GetPerformanceCounter()+»»»,,»»»+Катал+Файл+»»»);»;

    КонецЕсли;

    Сервис.ДобавитьГлобальныйМодуль(»

    |Перем fn_mlgReader Экспорт;

    |

    |Процедура ПриНажатииКнопкиКлавиатуры(Конт,КодКлавиши,Alt,Shift,Ctrl,Символ,ФСО)

    |Если КодКлавиши<>192 Тогда Возврат; КонецЕсли;

    |Если Shift+Ctrl<>2 Тогда Возврат; КонецЕсли;»+РазделительСтрок+

    ОткрФорму+»

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

    Reply
  55. kleo19866

    спасибо за разработку, интересная

    Reply
  56. Lvbnhbq123

    Зачетно!

    Reply
  57. elektr

    спасибо за обработку, пригодилась.

    Reply

Leave a Comment

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