Link to home
Start Free TrialLog in
Avatar of roosiedb
roosiedb

asked on

Printing invoices in MS Word: from Paradox to Word...

HELP ME PLEASE...

In my invoice-application, I want to send data fromout a Paradox-table to Word. I really don't know how to do the following:

- I want to startup MS Word from my application,
- Open a specific template,
- Fill the fields with the data I selected en save the file as a Word-document.

Because I am a real beginner, I would like to ask you to give me an example which I can follow step by step...

Thank you very much for helping me...!!!

Jan van Barchum.
Avatar of elkiors
elkiors

Have a look at Dynamic Data Exchange (DDE) and OLE Automation in the Delphi help and MSWord help, that should give you some basic idea.
ASKER CERTIFIED SOLUTION
Avatar of RBertora
RBertora
Flag of United Kingdom of Great Britain and Northern Ireland image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
hello???
Avatar of roosiedb

ASKER

Sorry, I didn't read your answer yet, I will try to implement your solution this weekend. You'll hear from me soon, OK?

Jan.
ok :-)
Hi Roosiedb,
did that help you?
Rob ;-)
I'm very sorry again, I still did not try out your solution. I really will try it this weekend. Within 4 days I will reply... Hope you don't mind...

The only thing I still can not see in your code is the way to handle an x-number of invoice-details... It isn't known how much orderdetails there are for one invoice...

Greetz...
Because I did not tell you that I want to use this solution to export data for invoicing, I will only judge the first part of my problem, but it would be very nice when you can answer this accompanying problem too...

Greetz again...
1. connect to word

// to handle multiple invoices:

   Query1.Open; // to get your database records
   for lp := 1 to NumberOfInvoices do
   begin
   //open template
    WordApp.Documents.Open('c:template.doc');


   // run merge
     MergeFindReplace1Click(Sender); //use query1.fields to replace

   // save the docuemnt as your invoice number
     WordApp.ActiveDocument.SaveAs(FullFileNameInvoiceNumber);

     Query1.Next;
   end;


Hint: its a good idea to make sure template.doc is readonly
as you don't want to accidentally delete it

Rob ;-)
It almost works !!

Only when my Word-application was already there, the merge will work OK. But when I did not startup Word before I startup my Delphi-program, my application is not able to startup MS Word for me... Too bad!

I think something went wrong with these lines:

// connect or open word
  try
    WordApp := GetActiveOleObject('Word.Application');
  except on exception do
    WordApp := CreateOleObject('Word.Application');
  end;

When I run these lines, I get the following error-message: "Project Project1.exe raised exception class EOleSysError with message 'Bewerking is niet beschikbaar'. Process stopped. Use Step or Run to continue.".

NB. The message was in Dutch, it means something like "action not available".

So: please don't give up and continue with your help...

---

The other question (I will increase my points to 300 when this all works perfect!) was not completely understood I guess...

I didn't mean to use more than one invoice, but I want to fill more than one invoice-row. And every invoice can have a random number of rows. (For example: the first invoice does have 2 rows: the customer bought a book and a pencil; the second invoice does have 1 row: the customer bought ten magazines). I hope these examples will clear up the question.

Thank you very much for helping me !!!!

Jan van Barchum.
Hmm for thaat sort of thing you need to write out all the contents of the word document from scratch.. ie do not use find replace.

Here is a wrapper object I have written to help me generate word documents:

I have also included some sample so you can see how to use it

Rob;-)

unit MainDocTemplateFunctions;

{ This module or unit does not describe a form, therefore it is a nonvisual piece
  of code. It contains the declaration and implementation of a new object called
  MyWordAppObject. I use this object to wrap certain ole functions for word97.
  These functions correspond to all the functions exported by the DocumentsTemplate
  program. - very simple

  Note: There function called write database, which simply writes the variable stored in
        the fdatabasevalue variable to the open word document which is contained in the
        FWordApp variant variable.

  }


interface

uses classes,sysutils,Forms;




type  MyWordAppObject = class(TComponent)
      private
        FWordApp : Variant;
        FDatabaseValue : String;

      public
        Property WordApp : Variant read FWordApp Write FWordApp;
        property DatabaseValue : String read FDatabaseValue write FDatabaseValue;


        Constructor Create(AnOwner : TComponent); override;
        Destructor Destroy; override;


        function InsertDate : Boolean;
        function InsertDateTime : Boolean;
        function InsertTime : Boolean;

        function OpenHeader : Boolean;
        function CloseHeader : Boolean;
        function Tab : Boolean;
        function Enter : Boolean;
        function SetAlignmentLeft : Boolean;
        function SetAlignmentCenter : Boolean;
        function SetAlignmentRight : Boolean;
        function ToggleUnderLine : Boolean;
        function ToggleBold : Boolean;
        function ToggleItalic : Boolean;
        function OpenFooter : Boolean;
        function CloseFooter : Boolean;
        function NewParagraph : Boolean;

        function WriteString(S : String) : Boolean; virtual;
        function SetHeadingStyle(S:String): Boolean; {'Heading 1','Heading 2','Heading 3','Normal'}
        function InsertImage(FileName:String): Boolean;

        function SetFontSize(I : Integer): Boolean;


        function SetColor(I : Integer): Boolean;
        {1 = black 2 = blue 3 = aqua 4 = lime 5 = fuschia 6 = red 7 = yellow 8 = white 9 = navy 10 = teal 16 = silver}

        function SetFont(S : String): Boolean;
        {'Arial','Times New Roman','Arial Narrow','Arial Black','Courier','Courier New','Impact','Letter Gothic','Marigold','Symbol'}

        procedure ExecuteFunction(FunctionName : String);
        function PullStringParameter(FunctionName : String) : String;
        function PullIntegerParameter(FunctionName : String) : String;




//        functionRunFunction
     end;




//exports MyWordAppObject;
(*
exports Proc1, Proc2;

Then you can do this:

procedure runStrProc(procStrVar: string);
var proc : procedure;
begin
  proc:=GetProcAddress(hInstance,procStrVar);
  if @proc<>nil then proc;
end;

procedure TForm1.Button7Click(Sender: TObject);
begin
  showmessage('hello');
  ThisApp := MyWordAppObject.Create(Form1);
  showmessage('hello');
  ThisApp.WordApp := WordApp;
  ThisApp.WriteString('helloworld');

  ThisSyntaxApp := MyWordAppObjectSyntaxCheck.Create(ThisApp);
  ThisSyntaxApp.WriteString('............Hello');



end;

procedure TForm1.Button10Click(Sender: TObject);
begin
  ThisSyntaxApp.Destroy;
  ThisApp.Destroy;
end;

  *)

implementation


{ MyWordAppObject }

function MyWordAppObject.CloseFooter: Boolean;
begin
  FWordApp.ActiveDocument.ActiveWindow.ActivePane.View.Type := 1; //normalview
  result := True ;
end;

function MyWordAppObject.CloseHeader: Boolean;
begin
  FWordApp.ActiveDocument.ActiveWindow.ActivePane.View.Type := 1; // normalview
  result := True ;
end;

constructor MyWordAppObject.Create(AnOwner : TComponent);
begin
  inherited Create(AnOwner);
  FWordApp := 0;
end;

destructor MyWordAppObject.Destroy;
begin
  inherited Destroy;
end;

function MyWordAppObject.Enter: Boolean;
begin
  FWordApp.ActiveDocument.ActiveWindow.ActivePane.Selection.TypeText(#10);
  result := True ;
end;

procedure MyWordAppObject.ExecuteFunction(FunctionName: String);
var
  OpenBracketIndex : Integer;
  JustTheName : String;
begin
  OpenBracketIndex := Pos('(',FunctionName);
  if OpenBracketIndex <> 0 then
    JustTheName := Copy(FunctionName,1,OpenBracketIndex-1)
  else
    JustTheName := Copy(FunctionName,1,Length(FunctionName)-1);


  if uppercase(JustTheName) = 'INSERTDATE' then InsertDate;
  if uppercase(JustTheName) = 'INSERTDATETIME' then InsertDateTime;
  if uppercase(JustTheName) = 'INSERTTIME' then InsertTime;

  if uppercase(JustTheName) = 'WRITEDATABASE' then
  begin
    WriteString(FDatabaseValue);
  end;

  if uppercase(JustTheName) = 'OPENHEADER' then OpenHeader;
  if uppercase(JustTheName) = 'CLOSEHEADER' then CloseHeader;
  if uppercase(JustTheName) = 'TAB' then Tab;
  if uppercase(JustTheName) = 'ENTER'  then Enter;
  if uppercase(JustTheName) = 'SETALIGNMENTLEFT' then SetalignmentLeft;
  if uppercase(JustTheName) = 'SETALIGNMENTCENTER' then SetAlignmentCenter;
  if uppercase(JustTheName) = 'SETALIGNMENTRIGHT' then SetAlignmentRight;
  if uppercase(JustTheName) = 'TOGGLEUNDERLINE' then ToggleUnderLine;
  if uppercase(JustTheName) = 'TOGGLEBOLD' then Togglebold;
  if uppercase(JustTheName) = 'TOGGLEITALIC' then ToggleItalic;
  if uppercase(JustTheName) = 'OPENFOOTER' then OpenFooter;
  if uppercase(JustTheName) = 'CLOSEFOOTER' then CloseFooter;
  if uppercase(JustTheName) = 'NEWPARAGRAPH' then NewParagraph;
  if uppercase(JustTheName) = 'WRITESTRING' then WriteString(PullStringParameter(FunctionName));
  if uppercase(JustTheName) = 'SETHEADINGSTYLE' then SetHeadingStyle(PullStringParameter(FunctionName));
  if uppercase(JustTheName) = 'INSERTIMAGE' then InsertImage(PullStringParameter(FunctionName));
  if uppercase(JustTheName) = 'SETCOLOR' then SetColor(StrToInt(PullIntegerParameter(FunctionName)));
  if uppercase(JustTheName) = 'SETFONT' then SetFont(PullStringParameter(FunctionName));
  if uppercase(JustTheName) = 'SETFONTSIZE' then SetFontSize(StrToInt(PullIntegerParameter(FunctionName)));


end;

function MyWordAppObject.InsertDate: Boolean;
begin
  FWordApp.ActiveDocument.ActiveWindow.ActivePane.Selection.TypeText(FormatDateTime('mmmm dd yyyy',Date));
  result := True ;
end;

function MyWordAppObject.InsertDateTime: Boolean;
begin
  FWordApp.ActiveDocument.ActiveWindow.ActivePane.Selection.TypeText(FormatDateTime('mmm dd yyyy hh:nn:ss',Now));
  result := True ;
end;

function MyWordAppObject.InsertImage(FileName: String): Boolean;
begin
  FWordApp.ActiveDocument.ActiveWindow.ActivePane.Selection.TypeParagraph;  //enter
  FWordApp.ActiveDocument.ActiveWindow.ActivePane.Selection.InlineShapes.AddPicture(FileName,False,True);
  result := True ;
end;

function MyWordAppObject.InsertTime: Boolean;
begin
  FWordApp.ActiveDocument.ActiveWindow.ActivePane.Selection.TypeText(TimeToStr(Time));
  result := True ;
end;

function MyWordAppObject.NewParagraph: Boolean;
begin
  FWordApp.ActiveDocument.ActiveWindow.ActivePane.Selection.TypeParagraph;
  result := True ;
end;

function MyWordAppObject.OpenFooter: Boolean;
begin
  FWordApp.ActiveDocument.ActiveWindow.ActivePane.View.Type := 2; //not normalview
  FWordApp.ActiveDocument.ActiveWindow.ActivePane.View.SeekView := 10; // footer
  result := True ;
end;

function MyWordAppObject.OpenHeader: Boolean;
begin
  FWordApp.ActiveDocument.ActiveWindow.ActivePane.View.Type := 2; //not normalview
  FWordApp.ActiveDocument.ActiveWindow.ActivePane.View.SeekView := 9; //header
  result := True ;
end;

function MyWordAppObject.PullIntegerParameter(
  FunctionName: String): String;
var
  Index : Integer;
begin
  Index := pos('(',FunctionName);
  Delete(Functionname,1,Index);
  Index := pos(')',FunctionName);
  result := Copy(FunctionName,1,Index-1);
end;

function MyWordAppObject.PullStringParameter(FunctionName: String): String;
var
  Index : Integer;
  TempString : String;
begin
  Index := pos('"',FunctionName);
  Delete(Functionname,1,Index);
  Index := pos('"',FunctionName);
  TempString := Copy(FunctionName,1,Index-1);

// replace char177 with colon
  Index := pos(#177,TempString);
  while Index <> 0 do
  begin
    TempString[Index] := ':';
    Index := pos(#177,TempString)
  end;

  Result := TempString;
end;

function MyWordAppObject.SetAlignmentCenter: Boolean;
begin
  FWordApp.ActiveDocument.ActiveWindow.ActivePane.Selection.ParagraphFormat.Alignment := 1;
  result := True ;
end;

function MyWordAppObject.SetAlignmentLeft: Boolean;
begin
  FWordApp.ActiveDocument.ActiveWindow.ActivePane.Selection.ParagraphFormat.Alignment := 0;
  result := True ;
end;

function MyWordAppObject.SetAlignmentRight: Boolean;
begin
  FWordApp.ActiveDocument.ActiveWindow.ActivePane.Selection.ParagraphFormat.Alignment := 2;
  result := True ;
end;

function MyWordAppObject.SetColor(I: Integer): Boolean;
{1 = black 2 = blue 3 = aqua 4 = lime 5 = fuschia 6 = red 7 = yellow 8 = white 9 = navy 10 = teal 16 = silver}
begin
  FWordApp.ActiveDocument.ActiveWindow.ActivePane.Selection.Font.ColorIndex := I;
  result := True ;
end;

function MyWordAppObject.SetFont(S: String): Boolean;
{'Arial','Times New Roman','Arial Narrow','Arial Black','Courier','Courier New','Impact','Letter Gothic','Marigold','Symbol'}
begin
  FWordApp.ActiveDocument.ActiveWindow.ActivePane.Selection.Font.Name := S;
  result := True ;
end;

function MyWordAppObject.SetFontSize(I: Integer): Boolean;
begin
  FWordApp.ActiveDocument.ActiveWindow.ActivePane.Selection.Font.Size := I;
  result := True ;
end;

function MyWordAppObject.SetHeadingStyle(S: String): Boolean;
{'Heading 1','Heading 2','Heading 3','Normal'}
begin
  FWordApp.ActiveDocument.ActiveWindow.ActivePane.Selection.Range.Style := S;
  result := True ;
end;

function MyWordAppObject.Tab: Boolean;
begin
  FWordApp.ActiveDocument.ActiveWindow.ActivePane.Selection.TypeText(#9);
  result := True ;
end;

function MyWordAppObject.ToggleBold: Boolean;
begin
  FWordApp.ActiveDocument.ActiveWindow.ActivePane.Selection.font.Bold := 9999998;
  result := True ;
end;

function MyWordAppObject.ToggleItalic: Boolean;
begin
  FWordApp.ActiveDocument.ActiveWindow.ActivePane.Selection.font.Italic := 9999998;
  result := True ;
end;

function MyWordAppObject.ToggleUnderLine: Boolean;
begin
  if FWordApp.ActiveDocument.ActiveWindow.ActivePane.Selection.font.underline = 0 then
    FWordApp.ActiveDocument.ActiveWindow.ActivePane.Selection.font.underline := 1
  else
    FWordApp.ActiveDocument.ActiveWindow.ActivePane.Selection.font.underline := 0;
  result := True ;
end;



function MyWordAppObject.WriteString(S: String): Boolean;
begin
  FWordApp.ActiveDocument.ActiveWindow.ActivePane.Selection.TypeText(S);
  result := True ;
end;


end.



// how to use the unit:



uses MaindDocTemplateFunctions

var
  WordApp : Variant;
  ThisApp : MyWordAppObject;


BEGIN


// connect or open word using the WordApp Variant
  try
    WordApp := GetActiveOleObject('Word.Application');
  except on exception do
    WordApp := CreateOleObject('Word.Application');
  end;


// Create my wrapper object and assign it to the WordApp variant
  ThisApp := MyWordAppObject.Create(Form1);
  ThisApp.WordApp := WordApp;


// Now you can run any of the functions exported by my object

  with ThisApp do
  begin
    NewParagraph;
    NewParagraph;
    NewParagraph;
      WriteString(Query1.FieldByName('ClientName').AsString);
    NewParagraph;
       WriteString(Query1.FieldByName('ClientAddress1').AsString);
    NewParagraph;
     InsertDate;
    NewParagraph;
    NewParagraph;
      ToggleBold;
      WriteString("Document Ref # ");
      WriteString(Query1.FieldByName('EventNumber').AsString);
      ToggleBold;
    NewParagraph;
    NewParagraph;
      WriteString("Dear ");
      WriteString((Query1.FieldByName('EventNumber').AsString;
    NewParagraph;
    OpenHeader;
    SetAlignmentCenter;
    SetColor(6);
    WriteString("This is a red coloured centered header");
    CloseHeader;
    OpenFooter;
    WriteString("This is a footer my last example");
    CloseFooter;
    WriteString("Hello world");

    // add multiple database rows into document
    Query2.First;
    for Lp := 1 to Query2.RecordCount-1 do
    begin
      WriteString(Query2.FieldByName('InvoiceRow').AsString);
      NewParagraph;
      Query2.Next;
    end;

    NewParagraph;
    NewParagraph;
    SetAlignmentRight;
    InsertDate;
    NewParagraph;
    NewParagraph;
  end;
END;





// remember to free the object
    ThisApp.Destroy;


Hi,
I am going on 2-3 months holiday this friday. So please if you have any questions then ask them NOW.

otherwise please raise the points to 300 and grade this question as promissed.

Thanks
Rob ;-)
Thank you for your help these days and have a nice holiday! 2-3 MONTHS ?!?! WOW...

Greetings,
Jan van Barchum.
Thanks again...

If you don't mind, please tell me also how to print the document immediately after the fields are filled...

Is something like WordApp.ActiveDocument.Print working right???

Greetings,
Jan van Barchum.
Hi this is your print help:
WordApp.ActiveDocument.PrintOut(Background, Append, Range, OutputFileName, From, To, Item, Copies, Pages, PageType, PrintToFile, Collate, FileName, ActivePrinterMacGX, ManualDuplexPrint)

expression   Required. An expression that returns an Application, Document, or Window object.

Background   Optional Variant. True to have the macro continue while Word prints the document.

Append   Optional Variant. True to append the specified document to the file name specified by the OutputFileName argument. False to overwrite the contents of OutputFileName.

Range   Optional Variant. The page range. Can be one of the following WdPrintOutRange constants: wdPrintAllDocument, wdPrintCurrentPage, wdPrintFromTo, wdPrintRangeOfPages, or wdPrintSelection.

OutputFileName   Optional Variant. If PrintToFile is True, this argument specifies the path and file name of the output file. This argument isn't available on the Macintosh unless QuickDraw GX is installed.

From   Optional Variant. The starting page number when Range is set to wdPrintFromTo.

To   Optional Variant. The ending page number when Range is set to wdPrintFromTo.

Item   Optional Variant. The item to be printed. Can be one of the following WdPrintOutItem constants: wdPrintAutoTextEntries, wdPrintComments, wdPrintDocumentContent, wdPrintKeyAssignments, wdPrintProperties, or wdPrintStyles.

Copies   Optional Variant. The number of copies to be printed.

Pages   Optional Variant. The page numbers and page ranges to be printed, separated by commas. For example, "2, 6-10" prints page 2 and pages 6 through 10.

PageType   Optional Variant. The type of pages to be printed. Can be one of the following WdPrintOutPages constants: wdPrintAllPages, wdPrintEvenPagesOnly, or wdPrintOddPagesOnly.

PrintToFile   Optional Variant. True to send printer instructions to a file. Make sure to specify a file name with OutputFileName. This argument isn't available on the Macintosh unless QuickDraw GX is installed.

Collate   Optional Variant. When printing multiple copies of a document, True to print all pages of the document before printing the next copy.

FileName   Optional Variant. The path and file name of the document to be printed. If this argument is omitted, Word prints the active document. Available only with the Application object.

ActivePrinterMacGX   Optional Variant. On the Macintosh, if QuickDraw GX is installed, specifies the printer to print to.

ManualDuplexPrint   Optional Variant. Not used in the U.S. English version of Microsoft Word.

ALSO RUN A SEARCH on your hard drive
for the file vbawrd*.hlp, if you have the file then you can get all the help yourself!

Rob;-)

P.S. thanks very much for the points!
Thank you again!
Hi Rob,

Here I am again with some points...

25 for this one:
- How can I terminate the Word application?

And another 25 for this question:
- How can I set a boolean for true when a specific piece of text has been found in a Word-document (something like "FIND")?

Thank you again...
I didn't have the help-file. Is there a website where I can download it?

Jan van Barchum.