ActiveDocument + Сервер 1C 8.3 = Разрыв соединения!




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

    Дёргать комом на сервере десктопные программы за гранью добра и зла, имхо.

    Reply
  2. fr13

    А что мешает использовать таблчиный документ как макет, заполнить, записать в поток, из потока в двоичный данные и дальше на клиента?

    Reply
  3. kiruha

    (1)

    А как нужно вызывать Word или Excel на сервере &

    Reply
  4. sergathome

    (3) НИКАК 0=0. На клиенте.

    Reply
  5. kiruha

    (4)

    Странно.

    А вот в SQL Server есть штатные способы взаимодействия. Тот же OPENROWSET/

    Да и во «вражеских» ERP штатно работают.

    Может проблема не в сервере ?

    Reply
  6. sergathome

    (5) Проблема в том, что, запуская десктопную программу в контексте сервера, вы разрушаете контекст сервера с вероятностью отнюдь ненулевой (и производитель об этом прямо предупреждает https://support.microsoft.com/ru-ru/help/257757/considerations-for-server-side-automation-of-office). Нет, можно, конечно пить и из лужи, но вот стоит ли ?

    Reply
  7. SlavaKron

    Мой комментарий напрямую не относится к теме, но лучше писать Word.Quit(0).

    Например следующий код не закрывает процесс Word:

    Word = Новый COMОбъект(«Word.Application»);
    Word.Documents.Add();
    Word.Selection.TypeText(«Привет!»);
    Word.Quit();
    Reply
  8. kare

    (7)Пробовал с «0», результат был тот же. Хотя конечно же лучше делать как Вы указали.

    Reply
  9. kare

    (1) Подскажите тогда каким образом получить такой макет на клиенте? макет держать в двоичных данных и передавать через временное хранилище? тогда уходит редактирование шаблона » на лету» через ActiveDocument что в данном решении не подходило.

    Reply
  10. kare

    (3) Сами объекты можно создавать на клиенте предварительно получив на сервере данные для заполнения. Опять же если только это у Вас динамический шаблон без ActiveDocument.

    Reply
  11. kare

    (2) Имеется ввиду вообще без WORD ? или из макета брать данные для заполнения шаблона на клиенте? тогда вопрос как передать макет из обработки с типом ActivDocument на клиент?

    Reply
  12. fr13

    (11) Сделать макет с типом Табличный документ. На сервере его заполнить. Дальше у ТД есть метод Записать. Записать можно в файл, а можно сразу в потоквпамяти. Так вот запись в поток и указать тип сохранения. Дальше у потока есть метод ЗакрытьИПолучитьДвоичныеДанные. Дальше ДД в хранилище и адрес этого хранилища передать на клиент. Никакой word не нужен. Единственное что, так это версия платформы 8.3.9+, но так как у Вас ЗУП 3.1, то скорей всего это ограничение не для Вас

    Reply
  13. kare

    (12) )) это здорово конечно, но а как же «Эта форма утверждена и она должна выглядеть именно так вплоть до форматирования» ?

    Reply
  14. fr13

    (13) Речь была не про утвержденную форму. Это уже отдельная история. Если сильно захотеть, то можно сделать. В конце концов *docx можно распаковать и через xpath менять что угодно. Опять же, тема отдельного разговора.

    Reply
  15. kare

    (14) согласен с docx, но это совсем другая история )))

    Reply
  16. sergathome

    (9)https://infostart.ru/public/270277/

    ;)) всё новое — испохабленное старое

    Reply
  17. sergathome

    (8) делать лучше так, как чел из 14 года советует ;))

    https://infostart.ru/public/270277/

    у него там, правда, тоже не без ошибок, но, думаю, разберётесь

    Reply
  18. AlX0id

    (3) Ком не нужен.

    1. Переименовываете в .zip

    2. Распаковываете во временную папочку обычным архиватором.

    3. Заменяете в нужных файликах требуемые параметры.

    4. Запаковываете обратно.

    5. Переименовываете в .docx

    6. Профит.

    Reply
  19. kare

    (17)

    Каталог = ПараметрыСеанса.ТекущийПользователь.РабочийКаталог;
    Каталог = ?(Прав(Каталог,1) = «», Каталог, Каталог+»»);
    Макет = Документы.ДоговорыКонтрагентов.ПолучитьМакет(«ActiveDocument»);
    Макет.Записать(ПолноеИмяФайла);

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

    Reply
  20. kare

    (18) изящно, изначально docx должен быть?

    Reply
  21. sergathome

    (19)

    у него там, правда, тоже не без ошибок, но,

    не разобрались, ага. Понятненько.

    Короче, схема такая:

    1. Через ВременноеХранилище передаём макет на клиент.

    2. На клиенте получаем из ВХ и пишем в файл.

    3. Там же, на клиенте, открываем файл как угодно.

    Синхрофазотрон ?

    Reply
  22. kare

    (21) это понятно)) изначально макет есть что?

    (9) ранее писал:

    макет держать в двоичных данных и передавать через временное хранилище? тогда уходит редактирование шаблона » на лету» через ActiveDocument что в данном решении не подходило.
    Reply
  23. sergathome

    (22) Да что ж вы к этому несчастному АктивДокументу привязались. Двоичные данные он и пох. Укажете при записи файла расширение ему .дос и будет он вордом открываться как миленький. Есстно нужно правильный док указать — тот же, какой при помещении в макет был.

    Reply
  24. kare

    (23) если на сервере то в данном случае вызовется объект Word 32x что приведет к описанным в топе проблемам) в том вся соль что макет нужен для редактирования «на лету» если бы все было в двоичных данных топика бы здесь не появилось) Макет(AD) в том виде в котором он используется можно записать только на сервере, конечно без zip и тд и тп. Вариантов реализаций данного функционала масса. Но здесь конкретный случай. Переписывать все ПФ на двоичные данные и заполнение на клиенте это уже совсем другой выход.

    Reply
  25. sergathome

    (24) Вызовется тот, который вы ему скажете. Вы правильно написали всё про 32-64, только на клиенте половины проблем не будет 😉

    Reply
  26. kare

    (25) если двоичные данные .записать(**.doc) как он и был помещен туда на выходе мы получим 32х, про клиент — согласен %)

    Reply
  27. sergathome

    (26) да что же вы ересь-то такую несусветную пишете 🙂 Файл — он просто вордовский файл, он не бывает 32 или 64 и откроется он тем вордом, который у вас на клиенте стоит.

    Reply
  28. kare

    (27)почему же? я про сервер говорю. ActiveDocument вызывает неявно экземпляр COM (x32) и поэтому все дальнейшие манипуляции нужно делать учитывая это особенность.

    Reply
  29. sergathome

    (28)

    //На клиенте получаем макет и заполняем его предварительно полученными данными
    ДвоичныеДанныеМакета = ПолучитьИзВременногоХранилища(ПолучитьМакетСКлиента(«ПисьмоНаОплатуWord»));
    ИмяВрем = ПолучитьИмяВременногоФайла(«.docx»);
    ДвоичныеДанныеМакета.Записать(ИмяВрем);
    Попытка
    Документ = ПолучитьCOMОбъект(ИмяВрем);
    Для Каждого Параметр из СтруктураПараметров.Значение Цикл
    // пример:
    Selection = Документ.Content;
    Selection.Find.Execute(«[«+Параметр.Ключ+»]»,Ложь,Истина,Ложь,,,Истина,,Ложь,Параметр.Значение,2);
    КонецЦикла;
    Документ.Application.Visible = Истина;
    Документ.Application.WindowState = 2;
    Документ.Application.WindowState = 1;
    Документ.Activate();
    Исключение
    …..
    
    &НаСервереБезКонтекста
    Функция ПолучитьМакетСКлиента(Имя)
    Возврат ПоместитьВоВременноеХранилище(Документы.ПисьмоНаОплату.ПолучитьМакет(«ПисьмоНаОплатуWordДвоичныеДанные»));
    КонецФункции

    Показать

    Какие тут особенности ? ПолучитьСОМОбъект, который есть

    Функция  ПолучитьСОМОбъект(ИмяФайла)
    Word = Новый COMОбъект(«Word.Application»);
    Word.Displayalerts = 0;
    ДокументWord = Word.Application.Documents.Open(ИмяФайла);
    Возврат ДокументWord;
    КонецФункции

    ?

    И, главное, даже если оно рухнет, по неважно даже каким причинам, то сервер не завалится.

    Reply
  30. kembrik

    (13) Для этого надо использовать LaTeX

    Надоели мне постоянные танцы вокруг Офиса, делаю сейчас маленькую подсистемку, собирающую формы сразу в PDF

    Reply
  31. kare

    (29) здесь двоичные данные записанные НА КЛИЕНТЕ в DOCX, капитан. Зачем мне то что я говорил (9) ?

    макет держать в двоичных данных и передавать через временное хранилище? тогда уходит редактирование шаблона » на лету» через ActiveDocument что в данном решении не подходило.

    Может сделаете тоже самое только на сервере и с doc?без двоичных данных? в чем холивар ?

    Reply
  32. AlX0id

    (20)

    Да, xlsx и docx по сути упакованные xml-ки. А вот doc и xls — нет.

    Reply
  33. kare

    (29) То что данное решение имеет свои плюсы так как выполняется на клиенте это факт. Но я разве ставил под сомнение это?

    Reply
  34. sergathome

    (31) Здесь ваш АктивДокумент, который есть двоичные данные вордового файла, по-факту. Что же вы никак это не вкурите-то///

    АктивДокумент = ДвоичныеДанные(«ИмяФайла.doc»)

    Reply
  35. kare

    (32)ну для этого надо было тогда все шаблоны сохранить из пф потом пересохранить в docx и загрузить обратно в двоичные данные) как новое решение отлично подойдет.

    Reply
  36. kare

    (34) Мне не понятно к чему Вы пишите про двоичные данные и docx если в статье идет про ActiveDocument + &НаСервере ?

    Reply
  37. kare

    (30) Ждем публикацию, интересно будет взглянуть.

    Reply
  38. sergathome

    (36) пытался объяснить как то же самое сделать на клиенте. сдаюсь. не смог.

    Reply
  39. kare

    (38) я правильно понимаю Вас? Вы рекомендуете все шаблоны переписать на клиент ручками пересохранив Макет в doc потом загрузив обратно в макет двоичные данные далее переписать везде код для работы с клиентом и организовать заполнение на клиенте? и это все из за дерганья com на серевере? риали?

    Reply
  40. kare

    (39) я побоялся это описывать @sergathome . Конечно на клиенте в половины случаев все разное.

    Reply
  41. sergathome

    (40) исполните вот это и вкурите уже что такое волшебный ваш ActiveDocument

    &НаКлиенте
    Процедура ОткрытьАктивДокумент()
    //На клиенте получаем макет и заполняем его предварительно полученными данными
    ДвоичныеДанныеМакета = ПолучитьИзВременногоХранилища(ПолучитьМакетСКлиента(«Макет»));
    ИмяВрем = ПолучитьИмяВременногоФайла(«.doc»);
    ДвоичныеДанныеМакета.Записать(ИмяВрем);
    
    Документ = ПолучитьCOMОбъект(ИмяВрем);
    Документ.Application.Visible = Истина;
    Документ.Application.WindowState = 2;
    Документ.Application.WindowState = 1;
    Документ.Activate();
    КонецПроцедуры
    
    &НаКлиенте
    Функция  ПолучитьСОМОбъект(ИмяФайла)
    Word = Новый COMОбъект(«Word.Application»);
    Word.Displayalerts = 0;
    ДокументWord = Word.Application.Documents.Open(ИмяФайла);
    Возврат ДокументWord;
    КонецФункции
    
    &НаСервереБезКонтекста
    Функция ПолучитьМакетСКлиента(Имя)
    // Получаем макет и преобразуем его в двоичные данные
    лМакетОболочка = ПолучитьОбщийМакет(Имя);
    ИмяВрем = ПолучитьИмяВременногоФайла(«.doc»);
    лМакетОболочка.Записать(ИмяВрем);
    лМакет = Новый ДвоичныеДанные(ИмяВрем);
    Возврат ПоместитьВоВременноеХранилище(лМакет);
    КонецФункции
    

    Показать

    Насчет рили будете смеяцо когда сервак завалится при паре сотне юзеров в базах ;)) и будет падать каждые полчаса…

    Reply
  42. sergathome

    (42)

    будете смеяцо когда сервак завалится при паре сотне юзеров в базах ;)) и будет падать каждые полчаса…

    рецепт 18 работает только для docx, на минуточку…

    Reply
  43. kare

    (43)

    -Владимир Владимирович, женщина может стать президентом ?

    -Нет!

    -Почему же?

    -Я же не женщина.

    можно АД сохранить в файл и передать на клиент без участия COM. Только всю логику в каждой обработке заполнения придется переделать под клиент когда она сделана под СЕРВЕР. из за того что бы не менять пару строчек кода и опять про преимущества клиента почитайте (39).

    Reply
  44. kare

    (44) Опечатка (39)

    смеяцо

    153 пользователя полет нормальный.

    Reply
  45. sergathome

    (45) это свежий аргумент. ушёл думать. раньше следующего понедельника не ждите.

    Reply
  46. kare

    (47)будем скучать, возвращайтесь.

    Reply
  47. kare

    (43)

    Насчет рили будете смеяцо когда сервак завалится при паре сотне юзеров в базах ;)) и будет падать каждые полчаса…

    я не смеюсь, серьезно. Просто я не понял : «Переделываем все на клиент !!!!!!» когда проблема решилась и больше не возвращалась.

    Reply
  48. AlX0id

    (39)

    А нахрена вообще тогда ставили задачу по формированию документа в ворде? )

    Я не говорю, что документы ворда суперские и замечательные. Но если поставили такую задачу — можно решить так. И да — это, конечно же, не универсальное решение.

    Reply
  49. kare

    (50) ))) Ворд жил жив и будет жив) актуш)

    Reply
  50. zeegin

    (20) А вы не смотрели как это сделано в БСП 3.0.1?

    > Существенно ускорено и повышена стабильность формирования печатных форм в формате офисных документов. Команды печати в этом формате теперь доступны во всех видах клиентов в операционных системах семейства Linux, Mac OS, а также в веб-клиенте.

    http://downloads.v8.1c.ru/content//SSL/3_0_1_189/change.htm

    Теперь собственно используется Office Open XML который разбирается и заполняется на сервере как zip архив.

    Reply
  51. klinval

    Конечно прикольно когда проблему описанную в моей статье кто-то решает другим способом, а потом этот другой способ тоже пробуют переделать (плюс параллельно обоснуют и докапываются до причины ошибки)…

    Я только одного не понял: а создание папок проблему уже не решает?

    C:WindowsSysWOW64configsystemprofileDesktop

    C:WindowsSystem32configsystemprofileDesktop

    Плюс можно банально отказаться от ActiveDocument и работать с двоичными данными. Пробовали?

    См. тут и тут

    Reply
  52. kare

    (54)

    1)изначально были в системе до появления «сбоя».

    2)Да.

    Reply
  53. kiruha

    а нельзя ли

    вместо

    Word = Новый COMОбъект(«Word.Application»);
    Word.Displayalerts = 0;
    ДокументWord = Word.Application.Documents.Open(ВременныйФайл); /
    

    изменить на

    Word = ПолучитьCOMОбъект(ВременныйФайл);
    

    тк создание COMОбъект в большом количестве приводит к невозможности его создания

    При ПолучитьCOMОбъект такого не происходит, даже когда Новый COMОбъект уже не работает

    Reply
  54. kare

    (56)можно , в коде указанно.

    ДокументWord = Word.Application.Documents.Open(ВременныйФайл); // Можно попробовать через ПолучитьCOMОбъект().
    Reply
  55. aspirator23

    Вроде бы все вопросы задали. Остался главный. А что это за фильм с Траволтой? -:)

    Reply
  56. kare

    (58)криминальное чтиво))

    Reply
  57. klinval

    (55) Нашёл свой же комментарий (тут: https://infostart.ru/public/407448/):

    Макет = Документы.ДоговорыКонтрагентов.ПолучитьМакет(«ActiveDocument»);

    Макет.Записать(ПолноеИмяФайла);

    Так вот как оказывается можно ещё сохранить! Не заметил, что у ОболочкаActiveDocument есть метод Записать. Обязательно обновите статью, т.к. многие сваливаются на методе SaveAs, а Записать() у ОболочкаActiveDocument фактически является альтернативой

    Сама статья тут: https://infostart.ru/public/270277/

    Ни у меня ни у CeHbKA эту проблему хоть и касались, но так полно и подробно как у вас и у https://infostart.ru/public/568913/ не разбиралась, поэтому по любому плюс за статью))

    Reply

Leave a Comment

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