среда, 6 ноября 2013 г.

Работа с документами Microsoft Word (создание отчетности)

   В этой статье я хочу поговорить об очередной интересной полезности, такой как создание отчетности в Microsoft Word. Недавно передо мной стала задача, автоматизировать процесс заключения договоров у меня на работе, и последующего их хранения и учета в электронном виде.
   Пришлось перелопатить конечно много информации, но на выходе неожиданно для самого себя получилось очень простое,
компактное и я бы даже сказал универсальное решение, которым бы я хотел поделиться.
   Многие кто по долгу работы сталкивается с заключением договоров, знает, что даже работая с типовыми их формами, все равно возникают сложности, на предмет, то пропустишь, и забудешь вбить какие то данные, то вобьешь их не туда куда надо, бывает даже и такое, когда у тебя очередь и буквально разрываешься на части, и о возможности сконцентрироваться, чтобы качественно обслужить текущего клиента, можно только мечтать. Ошибся, переписываешь все заново, опять куча потерянного времени, а особенно под конец дня вообще, жесть. В общем решил я положить этому всему край, и в очередной выходной занялся реализацией задуманного. Задача стояла следующая, необходимо было создать некую форму документа с полями, куда бы я мог вбивать данные по договору, а потом, по нажатию клавиши смог бы либо сохранить, либо распечатать, уже готовый договор.
   Полный листинг получившегося приложения я приводить, здесь не буду по скольку всего очень много, а небольшой примерчик доступный для понимания мы рассмотрим, тем паче, что подробно рассмотрев его, любой из вас самостоятельно сможет создавать фишки с отчетностью даже еще круче чем получилось у меня.

   Итак приступим:

  Работать мы будем с шаблоном документа MS Word, для этого создаем вордовский документ, набираем в нем форму нашего договора, только вместо данных, которые мы будем перемещать в документ из нашей формы, впишем имена произвольных переменных. Допустим вместо даты, напишем слово date, Ф.И.О исполнителя - "FIO1", Ф.И.О заказчика - "FIO2" и.т.д.
   Чтобы легче было ориентироваться, ниже привожу скринчик, ориентировочного шаблона:

   Почему именно так и для чего все это надо, забегая немного вперед поясню. В дальнейшем, 
мы с вами, напишем функцию, которая будет искать эти значения и заменять на то что мы будем вводить в поля нашей формы. Вот так все просто). Записываем в Edit1, Ф.И.О. клиента , функция находит абзац, находит слово FIO2, и заменяет его на содержимое из нашего Edit1.   
   Далее, когда шаблон готов, сохраняем документ в формате .dot присваиваем ему какое нибудь имя, (я например свой назвал просто - "Договор") и для удобства работы сохранил его на диск D:\\ 
   Все с шаблоном разобрались, теперь переходим непосредственно к программированию.

1) Создаем новый проект и помещаем на форму ряд компонентов (как на скрине ниже),


подключаем в Uses библиотеку ComObj, объявляем глобальные переменные:

var 
Dogovor,W:Variant;
Text:String; 

и приступим к настройке компонентов на форме:

а) Кликаем компонент SaveDialog1, переходим к инспектору объектов, где в его свойстве Filter - прописываем Документ Microsoft Word|*.doc ;
б) В список Items компонента Combobox1, записываем какие нибудь города, например тот где вы живете или работаете). Свойство ItemIndex устанавливаете в 0;

2) Теперь создадим функцию, о которой мы говорили выше, она будет искать и осуществлять замену, для этого в разделе private пропишем: function Repl(Atx, B, C:String):String;
нажимаем Ctrl+Shift+C и описываем ее:

function TForm3.Repl(Atx, B, C: String):String; 
var 
F1,F2,F3:String; 
begin 
F1:=''; 
F2:=Atx; 
F3:=Atx; 
while 
Pos(B, F2)>0 do 
begin 
F2:=Copy(F2, Pos (B, F2), (Length(F2)- Pos(B, F2))+1); 
F1:=Copy(F3, 1, Length(F3) - Length(F2))+C; 
delete(F2, Pos (B, F2), Length(B)); 
F3:=F1+F2; 
end; 
result:=F3; 
end; 

 3) В событии OnCreate на форме, внесем настройки в DateTimePicker1 и DateTimePicker2, чтобы при запуске они показывали текущую дату:

procedure TForm1.FormCreate(Sender: TObject); 
begin 
DateTimePicker1.Date:=Now; 
DateTimePicker2.Date:=Now; 
end;

4) В событии OnClick, на кнопке "Печать" пишем:

procedure TForm1.Button1Click(Sender: TObject);
var
Text :String;
begin
D:=CreateOleObject('Word.Application'); //Создаем OLE объект;
D.Documents.Open('D:\Договор.dot');  //Загружаем для работы наш шаблон договора;
D.Options.CheckSpellingAsYouType:=false; //Отключение правописания и
D.Options.CheckGrammarAsYouType:=false;  //грамматики;


W:=D.Documents.Item(1);

text:=W.Paragraphs.Item(1).Range.Text; //Замена Num, на содержимое Edit1;
text:=repl(text,'Num',Edit1.Text);
W.Paragraphs.Item(1).Range.Text:=text;

text:=W.Paragraphs.Item(3).Range.Text;  //Замена Gorod в договоре на содержимое  
text:=repl(text,'Gorod',Combobox1.Text);  //Combobox1;
W.Paragraphs.Item(3).Range.Text:=text;

text:=W.Paragraphs.Item(3).Range.Text; //Замена Date1 в договоре на содержимое Q;
text:=repl(text,'Date1',DateToStr(DateTimePicker1.Date));
W.Paragraphs.Item(3).Range.Text:=text;

text:=W.Paragraphs.Item(5).Range.Text; //Замена FIO1, на содержимое Edit2;
text:=repl(text,'FIO1',Edit2.Text);
W.Paragraphs.Item(5).Range.Text:=text;

text:=W.Paragraphs.Item(7).Range.Text; //Замена FIO2, на содержимое Edit5;
text:=repl(text,'FIO2',Edit5.Text);
W.Paragraphs.Item(7).Range.Text:=text;

text:=W.Paragraphs.Item(12).Range.Text; //Замена Tovar, на содержимое Edit6;
text:=repl(text,'Tovar',Edit6.Text);
W.Paragraphs.Item(12).Range.Text:=text;

text:=W.Paragraphs.Item(14).Range.Text; //Замена Addr, на содержимое Edit3;
text:=repl(text,'Addr',Edit3.Text);
W.Paragraphs.Item(14).Range.Text:=text;

text:=W.Paragraphs.Item(15).Range.Text; //Замена Tel, на содержимое Edit4;
text:=repl(text,'Tel',Edit4.Text);
W.Paragraphs.Item(15).Range.Text:=text;

text:=W.Paragraphs.Item(18).Range.Text; //Замена Price, на содержимое Edit7;
text:=repl(text,'Price',Edit7.Text);
W.Paragraphs.Item(18).Range.Text:=text;

text:=W.Paragraphs.Item(23).Range.Text; //Замена Date2, на содержимое DateTimePicker2;
text:=repl(text,'Date2',DateToStr(DateTimePicker2.Date));
W.Paragraphs.Item(23).Range.Text:=text;

text:=W.Paragraphs.Item(27).Range.Text; //Замена FIO3, на содержимое Edit5;
text:=repl(text,'FIO3',Edit5.Text);
W.Paragraphs.Item(27).Range.Text:=text;

text:=W.Paragraphs.Item(27).Range.Text; //Замена FIO4, на содержимое Edit2;
text:=repl(text,'FIO4',Edit2.Text);
W.Paragraphs.Item(27).Range.Text:=text;

if PrintDialog1.Execute then  //Печать документа;
D.ActiveDocument.PrintOut;

D.Application.ActiveDocument.Close(false); //Закрываем документ, не сохраняем изменения;
D.Quit; //Освобождаем переменную;
end;

5) В событии OnClick, на кнопке "Сохранить" пишем все тоже самое, только в самом начале, после begin, зададим параметр FileName для компонента SaveDialog1, чтобы при сохранении договоров, в имя файла автоматом вбивался номер и дата документа). А в конце опишем опции сохранения, вместо опций печати:

procedure TForm1.Button1Click(Sender: TObject);
var
Text :String;
begin

SaveDialog1.FileName:=('Договор '+'№ '+Edit1.Text+' от  '+DateToStr(DateTimePicker1.Date)+'.doc');

D:=CreateOleObject('Word.Application'); //Создаем OLE объект;
D.Documents.Open('D:\Договор.dot');  //Загружаем для работы наш шаблон договора;
D.Options.CheckSpellingAsYouType:=false; //Отключение правописания и
D.Options.CheckGrammarAsYouType:=false;  //грамматики;

W:=D.Documents.Item(1);

text:=W.Paragraphs.Item(1).Range.Text; //Замена Num, на содержимое Edit1;
text:=repl(text,'Num',Edit1.Text);
W.Paragraphs.Item(1).Range.Text:=text;

text:=W.Paragraphs.Item(3).Range.Text; //Замена Gorod в договоре на содержимое    
text:=repl(text,'Gorod',Combobox1.Text); //Combobox1;
W.Paragraphs.Item(3).Range.Text:=text;

text:=W.Paragraphs.Item(3).Range.Text; //Замена Date1 в договоре на содержимое Q;
text:=repl(text,'Date1',DateToStr(DateTimePicker1.Date));
W.Paragraphs.Item(3).Range.Text:=text;

text:=W.Paragraphs.Item(5).Range.Text; //Замена FIO1, на содержимое Edit2;
text:=repl(text,'FIO1',Edit2.Text);
W.Paragraphs.Item(5).Range.Text:=text;

text:=W.Paragraphs.Item(7).Range.Text; //Замена FIO2, на содержимое Edit5;
text:=repl(text,'FIO2',Edit5.Text);
W.Paragraphs.Item(7).Range.Text:=text;

text:=W.Paragraphs.Item(12).Range.Text; //Замена Tovar, на содержимое Edit6;
text:=repl(text,'Tovar',Edit6.Text);
W.Paragraphs.Item(12).Range.Text:=text;

text:=W.Paragraphs.Item(14).Range.Text; //Замена Addr, на содержимое Edit3;
text:=repl(text,'Addr',Edit3.Text);
W.Paragraphs.Item(14).Range.Text:=text;

text:=W.Paragraphs.Item(15).Range.Text; //Замена Tel, на содержимое Edit4;
text:=repl(text,'Tel',Edit4.Text);
W.Paragraphs.Item(15).Range.Text:=text;

text:=W.Paragraphs.Item(18).Range.Text; //Замена Price, на содержимое Edit7;
text:=repl(text,'Price',Edit7.Text);
W.Paragraphs.Item(18).Range.Text:=text;

text:=W.Paragraphs.Item(23).Range.Text; //Замена Date2, на содержимое DateTimePicker2;
text:=repl(text,'Date2',DateToStr(DateTimePicker2.Date));
W.Paragraphs.Item(23).Range.Text:=text;

text:=W.Paragraphs.Item(27).Range.Text; //Замена FIO3, на содержимое Edit5;
text:=repl(text,'FIO3',Edit5.Text);
W.Paragraphs.Item(27).Range.Text:=text;

text:=W.Paragraphs.Item(27).Range.Text; //Замена FIO4, на содержимое Edit2;
text:=repl(text,'FIO4',Edit2.Text);
W.Paragraphs.Item(27).Range.Text:=text;

if SaveDialog1.Execute then  //Печать документа;
begin
D.ActiveDocument.SaveAs(SaveDialog1.FileName, $00000000);
ShowMessage('Файл сохранен в следующей директории: '+SaveDialog1.FileName);
end;

D.Application.ActiveDocument.Close(false); //Закрываем документ, не сохраняем изменения;
D.Quit; //Освобождаем переменную D;
end;



   Теперь можно запускать проект и любоваться результатом). Если все делалось в той последовательности, как описано в статье, то все должно работать как часы, ибо пример рабочий на все 100%, а код копировался прямо из листинга.
 
   Великовата получилась статейка, но из песни слов не выкинешь, а дорогу осилит идущий).


Кстати:


Если необходимо, можно установить ориентацию листа:


D.Application.Selection.PageSetup.Orientation:= $00000000; //Ориентация - портрет; если нужен ландшафт - $00000001;

и размеры полей:

D.Application.Selection.PageSetup.LeftMargin:=56; //отступ слева "2,5 сантиметра";
D.Application.Selection.PageSetup.RightMargin:=28; //отступ справа "1,0 сантиметр";
D.Application.Selection.PageSetup.TopMargin:=28;  //отступ сверху "1.0 сантиметр";
D.Application.Selection.PageSetup.BottomMargin:=42; //отступ снизу "1,5 сантиметра";



Единицы измерения - пункты (1 см = 28,35 п).


Вот так, например можно установить жирность шрифта, размер и выравнивание:


W.Paragraphs.Item(?).Range.Font.Bold:=True;
W.Paragraphs.Item(?).Range.Font.Size:=13;
W.Paragraphs.Item(?).Alignment:=$00000000; //Выравнивание по левому краю; если нужно
по центру - $00000001; и т.д. значения некоторых констант приведены ниже:



wdAlignParagraphLeft = 000000;
wdAlignParagraphCenter = 000001;
wdAlignParagraphRight = 000002;
wdAlignParagraphJustify = 000003;
wdAlignParagraphDistribute = 000004;
wdAlignParagraphJustifyMed = 000005;
wdAlignParagraphJustifyHi = 000007;
wdAlignParagraphJustifyLow = 000008;
wdAlignParagraphThaiJustify = 000009;



Чтобы было удобней пользоваться программой в последующем, файл шаблона можно поместить в папке с экзешником проекта. А для обращения к нему воспользоваться следующим кодом:


var
FName, Path:String
begin
FName:='Договор.dot';
Path:=(ExtractFilePath(Application.ExeName)+'\'+FName);


D.Documents.Open(Path);

...
...
...


end;


Теперь куда бы ни был перемещен экзешник с программой, если вордовский шаблон находится с ним в одной папке, он будет без проблем открыт и использован. И не нужно никаких перезаписей путей в листинге.























3 комментария:

  1. Однако метод, наконец то разобрался. Благодарю

    ОтветитьУдалить
    Ответы
    1. Кстати на мой взгляд удобнее использовать FastReport для отчетов (RadStudio Embarcadero), мощная штуковина, там все более понятно, диапазон возможностей гораздо шире и от офиса майкрософтского не зависишь.

      Удалить

Примечание. Отправлять комментарии могут только участники этого блога.