Работа с MS Word из 1С "для самых маленьких"




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

57 Comments

  1. maXon777

    Прекрасный обзор! Спасибо!

    Очень понравился подход с программным заполнением таблицы путем добавления строк!

    Reply ↓
  2. GOOD256

    Огромное спасибо!!!

    Reply ↓
  3. pvl_mksv

    Коротко и по-теме! +

    Reply ↓
  4. gradi

    В ворд2013 глюков с DOCVARIABLE не замечал.

    Reply ↓
  5. DJDUH

    Самая «засада» MSWord — это вставить значение в шейпы или иные надписи и картинки — сколько не игрался так и не получилось, найти выход — находит нужное, а значение вставить фига(

    Reply ↓
  6. director04

    Поставил плюс за краткость. И хотел бы еще одни плюс за заголовок.

    Хотя,… есть тут пара засранцев, которые обязательно нагадят, что де «все это всем давно известно…»

    Спасибо, плюс

    Reply ↓
  7. Сурикат

    (4) gradi,

    На небольших документов действительно все хорошо работает (на небольших я имею ввиду несколько десятков страниц). Когда количество приближается к 100 возможно непонятное поведение параметров. По использованию полей во мне больше говорит пользователь, который в свое время много с ними мучился =)

    Reply ↓
  8. Сурикат

    (5) DJDUH,

    К сожалению или к счастью с таким не сталкивался =)

    Могу только посоветовать покурить MSDN в части свойств и методов указанных вами коллекций. Если получиться, дайте знать. Добавлю к публикации =) Ну или сами созреете для написания кратенького гайда =)

    Reply ↓
  9. gradi

    (5) DJDUH, у вас не получалось именно вставить значение или добиться его отображения в документе?

    Reply ↓
  10. nni93

    Полезно, спасибо =)

    Reply ↓
  11. AndMax

    Коротко и красиво, спасибо, +

    Reply ↓
  12. stilet

    Подскажите, может кто нибудь сталкивался — как изменить цвет фона у какого нибудь набора символов (скажем двух или трех слов)

    Reply ↓
  13. Сурикат

    (12) stilet, а он у вас все время разный?

    Reply ↓
  14. v3rter

    Базовый метод работы с Word — записываем макрос с нужными действиями, читаем его, читаем хелп к нужным командам, чистим макрос от лишних действий и параметров, копируем в 1С, добавляя «ДокументВорд.» перед командами.

    По поводу параметров — был и есть старый дедовский метод, когда параметр вставлялся в виде текста типа [Мой_параметр], {Мой_параметр}, [{(%%Мой_параметр%%)}] — на выбор, затем командной поиска и замены самого ворда параметры меняются на значения.

    Кстати, проверяли: Application.Visible = False вначале и Application.Visible = True в конце действий ускоряет или нет?

    Reply ↓
  15. v3rter

    (12) stilet, если выделенного текста желтым, то

    Selection.Range.HighlightColorIndex = wdYellow
    Reply ↓
  16. sudmorsh

    Очень полезно, спасибо 😉

    Reply ↓
  17. v3rter

    Записал в ворде макрос замены текста во всем документе, кроме колонтитулов.

    Selection.Find.ClearFormatting

    Selection.Find.Replacement.ClearFormatting

    With Selection.Find

    .Text = «что»

    .Replacement.Text = «на что»

    .Forward = True

    .Wrap = wdFindContinue

    .Format = False

    .MatchCase = False

    .MatchWholeWord = False

    .MatchWildcards = False

    .MatchSoundsLike = False

    .MatchAllWordForms = False

    End With

    Selection.Find.Execute Replace:=wdReplaceAll

    Selection.EscapeKey

    Показать

    Возможно, будет полезен.

    Из хелпа:

    WdReplace

    Constant Value

    wdReplaceAll 2

    wdReplaceNone 0

    wdReplaceOne 1
    Reply ↓
  18. monkbest

    (17) v3rter, зачем искать в цикле?

    я делаю так

    ТекДок.Content.Find.Execute(«%параметр%,,,,,,,,,»мой текст»,2);

    меняет сразу во всем документе, работает гораздо быстрее, чем искать в цикле

    Reply ↓
  19. v3rter

    (18) monkbest, ну так я на это и намекал. Ждём от автора версию 2.0

    Reply ↓
  20. Сурикат

    (14) v3rter,

    Application.Visible = False вначале и Application.Visible = True в конце действий не ускоряет процесс. Проверил =)

    Reply ↓
  21. isanka

    Спасибо за труды.

    Для старта очень познавательно.

    Вопрос.

    как прочитать текст содержащийся в полученной таблице ?

    и как заполнять таблицы в ворде данными не переписывая строки самой таблицы, а просто заполняя ее данными?

    (пробовала заполнять в цикле (Таблица.Cell(ИндексСтрока+1,ИндексСтолбец+1).Range().InsertAfter(Текст),

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

    или это из области фантастики?)

    Reply ↓
  22. Сурикат

    Приношу извинения за небольшие проблемы с форматированием =(( Подвел новый редактор.

    Reply ↓
  23. Сурикат

    (21) isanka,

    Мне другие способы не знакомы. Можно заполнять же только некоторые ячейки. Обращение по номеру строки/столбца это позволяет.

    Поищите как word с ADO дружит =) В Excel ADO дает неплохой прирост производительности. Если найдете, дайте знать =) Добавим в публикацию =))

    Reply ↓
  24. v3rter

    (21) isanka, Попробуйте Таблица.Cell(ИндексСтрока+1,ИндексСтолбец+1).Range().Text или Таблица.Cell(ИндексСтрока+1,ИндексСтолбец+1).Range.Text

    Всё это гуглится, но не на русском: https://msdn.microsoft.com/ru-ru/library/office/ff821612.aspx

    Reply ↓
  25. freud

    windows 10 + ms office

    1c выдает

    ОбъектВорд = Новый COMОбъект(«Word.Application»)

    Не удалось создать объект.

    Возможно, отсутствует соответствующее приложение.

    в чем причина

    Reply ↓
  26. sssss_aaaaa_2011

    (25) В том, что последние версии оффиса не имеют СОМ-объектов.

    Reply ↓
  27. Сурикат

    (25)А где выполняется код? на сервере или на клиенте?

    Reply ↓
  28. HalfZer0

    Я требую больше версий слова Стурктура, то есть Струкутра, то есть… ааааа!!!

    Reply ↓
  29. fish249

    Полезная статейка

    Reply ↓
  30. MiniGrad2014

    Не помещая документ в макет можно напрямую заполнять поля шаблона ворда?

    Reply ↓
  31. Tanis

    Доброе утро!

    Как раз очень нужный вопрос.

    Подскажите, плиз. Выполнение заполнения шаблона происходит на стороне клиента (пользователя) или на сервере?

    Если есть вариант заполнять именно на сервере, то как это можно реализовать?

    Спасибо!

    Reply ↓
  32. Сурикат

    На сервере можно заполнять только в том случае, если стоит на нем Word. Иначе только ковыряние в XML.

    Reply ↓
  33. Tanis

    Да. Поставили туда специально Ворд.

    А как осуществить это? подскажите, пожалуйста.

    Как передать макет на сервер, до его получения пользователем?

    Спасибо!

    Reply ↓
  34. tjurikov_ivan

    (33) В 1С ДО заполнение шаблонов на сервере происходит через фоновое задание

    Reply ↓
  35. user697645_gonchar.m

    Спасибо!

    Reply ↓
  36. Сурикат

    Эм…

    У вас 1с ДО?

    Можно и без фонового задания.

    Сохраняете в двоичные данные, а на сервере получаете.

    Если нужно пользовательский макет, то двоичные данные сериализуются

    Reply ↓
  37. Tanis

    Хм….

    Тогда еще вопрос, а как сохранить этот шаблон в двоичные данные, для передачи на сервер?

    Макет сделан как Актив документ.

    Word = ЭтотОбъект.ПолучитьМакет(«ШаблонВорда»).Получить();

    При этом сразу ошибка, что мол нет программы….

    Reply ↓
  38. Сурикат

    А зачем вам макет active document, если можно сразу сделать двоичные данные и передавать на сервер?

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

    Reply ↓
  39. Tanis

    Так макет же является — актив документ.

    В данном случае сохранение используется. Но могу сказать, что можно воспользоваться без сохранения. )))))

    Reply ↓
  40. Сурикат

    (39)

    Не используйте актив документ =) Это достаточно ущербная вещь, использование которой не дает никаких преимуществ, а добавляет только проблем.

    Reply ↓
  41. Tanis

    Добрый вечер!

    А чем они стали ущербные ((((

    Что предлагаете? Держать шаблон на диске или вообще перетаскивать в макет?

    Спасибо!

    Reply ↓
  42. Сурикат

    Ущербные потому что править их довольно-таки тяжело, неудобный интерфейс. Возможно я что-то не знаю или это субъективно…

    Предлагаю редактировать в Word и держать в макете с типом двоичные данные.

    Reply ↓
  43. artms

    А почему не напечатать на сервере? https://infostart.ru/public/675307/

    Reply ↓
  44. bondar_vy

    Коллеги, добрый день. Столкнулся с тем что в ЗУП 3.1 платформа 8.3.10.2667 при загрузке шаблона word 2013 не подтягиваются колонтитулы и рисунки. Может нужны какие-то специальные настройки в 1С или в word? Подскажите пожалуйста.

    Reply ↓
  45. Pavel Rodinchenko

    Спасибо за статью. Очень полезно

    Reply ↓
  46. AlexeyK1

    А что же самых маленьких не учим с пеленок за собой убирать?

    надо бы добавить уничтожение объектов и файлов.

    а так статья хорошая )))

    РS а то как открывать учим

    //Разумеется, получение COM-объекта нужно поместить в попытку. Мало ли, что-то пойдет не так.

    а как в Исключении прибить и почистить нет

    Reply ↓
  47. Сурикат

    (46)

    Я как-то верю в умные указатели…

    Думаете не стоит? =)

    Спасибо за замечание =)

    Reply ↓
  48. Forrest_Gump

    Подскажите а чем могут быть «тормоза»???…

    простенький шаблон договора 6 страниц… 30 параметров для замены…

    Reply ↓
  49. pchelov

    Метод «Execute» имеет ограничение на длину значения для замены в 255 символов, если будет больше, то появляется исключение. Я написал небольшую процедуру для выполнения замены частями:

    Процедура ВыполнитьЗаменуСОграничениемДлиныСтроки(вхДокументВорд, вхШаблонЗамены, вхЗначение)

    // Замена значений в ворде имеет ограничение на длину в 255 символов.

    // Разобьем значение на куски по 255 знаков

    МаксДлинаСтроки = 255;

    ИДШаблона = «@»+СтрЗаменить(Новый УникальныйИдентификатор, «-«, «»);

    МаксДлинаСтроки = МаксДлинаСтроки-СтрДлина(ИДШаблона);

    ТекШаблон = Строка(вхШаблонЗамены);

    НеРазбитоеЗначение = Строка(вхЗначение);

    МассивЧастейСтроки = Новый Массив;

    Пока СтрДлина(НеРазбитоеЗначение)>0 Цикл

    ЧастьНужнойДлины = Лев(НеРазбитоеЗначение, МаксДлинаСтроки);

    МассивЧастейСтроки.Добавить(ЧастьНужнойДлины);

    НеРазбитоеЗначение = Сред(НеРазбитоеЗначение, МаксДлинаСтроки+1, МаксДлинаСтроки);

    КонецЦикла;

    Для Каждого ЧастьСтроки Из МассивЧастейСтроки Цикл

    вхДокументВорд.Application.Selection.Find.Execute(ТекШаблон,,,,,,,,, ЧастьСтроки+ИДШаблона, 1);

    ТекШаблон = ИДШаблона;

    КонецЦикла;

    вхДокументВорд.Application.Selection.Find.Execute(ТекШаблон,,,,,,,,, «», 1); // Зачистим остатки шаблона

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

    Reply ↓
  50. user608357_elpst

    Добрый день. А не подскажите — можно ли в созданный документ *.docx в нижний колонтитул внести произвольный текст и номер страницы?

    Reply ↓
  51. named_Alexander

    А куда писать этот код ? Мне надо его в Справочник, модуль формы элемента поместить, чтоб Договор заполнялся данными из формы элемента. Создал в нужном мне справочнике макет с двоичными данными, потом в форме элемента создал кнопку и в событиях кнопки вставил код, Но он не работает там. Еще непонятно, где что выполняется, что на клиенте, а что на сервере ? Подскажите пожалуйста 1С УТ 11

    Reply ↓
  52. Сурикат

    (51)

    В УТ11 сложнее. На сервере или на клиенте у вас Word стоит?

    Reply ↓
  53. named_Alexander

    (52)

    На клиенте.

    Reply ↓
  54. user1132956

    Очень не понятно! решил впервые распечатать из Word, и что в итоге? Добавил файл ворд в макеты через двоичные данные, вставил код, сразу же заругался на «ДополнительныеПараметры.ВидДоговора» — что это такое, откуда брать и зачем? если уж называете статью, для самых меленьких, тогда хоть что-то объясняйте

    Reply ↓
  55. Сурикат

    (54)

    К сожалению, в одной статье не научить программированию и одновременно работе с word =(

    Если бы вы открыли в синтакспомощнике описание метода ПолучитьМакет, то увидели бы, что он принимает в качестве первого параметра имя макета с двочиными данными =)

    Reply ↓
  56. rjkjlptq

    Добрый день!

    Подскажите, как вывести все значения через запятую в строку без использования таблицы.

    Для каждого Параметр Из СтруктураПараметров.ТЧПостояннаяЧасть Цикл

    СтруктураПараметров.ДанныеШапка.Площадь = Формат(Параметр.Площадь,»ЧЦ=15; ЧДЦ=1″);

    КонецЦикла;

    Reply ↓
  57. Сурикат

    (56)

    как вывести все значения через запятую в строку без использования таблицы.

    Несовсем понятно какие данные на входе и какие должны быть на выходе…

    P.S.

    Возможно вам поможет СтрСоединить()

    Reply ↓

Leave a Comment

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