Como enviar cartas a todos tus clientes/proveedores desde VFP usando OLE con Word 97

Por Pedro Pascua
© Copyrights 1998 by FoxPress, All rights reserved
FoxPress, Abril 1998

En las siguientes líneas vamos a hacer una leve incursión en la programación del Word utilizando, como no, nuestra herramienta favorita, el Fox. El Word, desde hace ya unas cuantas versiones, viene siendo una herramienta bastante flexible en cuanto a la personalización a través de macros o de lenguajes como lo que se llamó WordBasic y que ha evolucionado para acabar integrándose en el Visual Basic for Applications (VBA). Lenguaje común de programación para todas las herramientas de Microsoft Office. Aunque este lenguaje está pensado para aumentar la funcionalidad de los macros en Word utilizando la sintaxis y metodología del Visual Basic, la mayoría de las propiedades y métodos de los objetos que emplea para controlar los documentos, pueden ser utilizados desde otros lenguajes, instanciándolos como clase OLE. Para ilustrar esta técnica, vamos a construir un sencillo formulario Fox para lanzar mailings a partir de los datos de una tabla. En el ejemplo, elegiremos primero entre la tabla de clientes o la de empleados. A continuación, seleccionaremos de la lista de nombres, aquellos que queremos incluir en el mailing. Y finalmente, escribiremos el texto común a las cartas. Al pulsar el botón Generar Documento Word, el programa abrirá una sesión de Word y nos creará un nuevo documento con una página para cada carta, incluyendo en la cabecera los datos del cliente o empleado extraídos de la tabla correspondiente y a continuación el texto que hemos escrito en el EditBox. Una vez creado el documento, podremos previsualizarlo, imprimirlo o guardarlo desde nuestro formulario pulsando los botones correspondientes. Para este ejemplo hemos utilizado el VFP5.0 y el Word97, que soporta VBA. No obstante, algunos de los métodos empleados funcionarían con versiones anteriores del Word.

Echemos un vistazo al código. En el Init del formulario creamos los cursores de clientes y empleados que contienen los datos de ejemplo para el mailing. En el destroy limpiaremos los cursores y variables empleados.


 


** Método Init() del Form
**
Public oWord

select cust_id,company ;
	from customer ;
	order by 2 ;
	into cursor cCustomer
select emp_id,;
	ALLT(employee.first_name)+;
	' '+ALLT(employee.last_name);
	from employee ;
	order by 2 ;
	into cursor cEmployee
		 
Thisform.List1.RowSource =  'cCustomer' 
_Screen.windowstate = 1

**  Método Destroy() del form
**
Release oWord
Use in cCustomer
Use in cEmployee
_Screen.windowstate = 2

Para visualizar mejor como nuestro código Fox interactúa con el Word, hemos creado el formulario de tipo Top Level y en el inicio de este, minimizamos la pantalla principal del VFP de modo que en el escritorio solo tengamos el formulario y podamos ver a la vez la ventana del Word cuando pulsemos el botón de previsualizar. En el InteractiveChange() del combo, reasignamos el origen de datos del ListBox:


 

********************************
** Combo1.InteractiveChange()  *
********************************

If This.DisplayValue = 1
   Thisform.List1.RowSource='cCustomer'
Else
   Thisform.List1.RowSource='cEmployee'
EndIf	
Thisform.List1.Refresh()

El listbox tiene la propiedad multiselect a true para que podamos seleccionar varios registros simultáneamente. Lo realmente importante de este ejemplo viene aquí, en el evento click del botón etiquetado Generar documento Word:

*************************
** CmdGenerar.Click()   *
*************************

PUBLIC oWord
#define cr chr(13)

oLista = ThisForm.list1
* Contamos el número de páginas (una por  
* cada registro seleccionado)
lnNumPag = 0
For lnLine = 1 To oLista.ListCount
  lnNumPag = lnNumPag+;
    +IIF(oLista.Selected(lnLine),1,0)
EndFor

* Comprobamos que hemos seleccionado
* al menos un registro
If lnNumPag = 0
  MessageBox("No hay registros ;
seleccionados",64,"Aviso")
  Return
EndIf

* instanciamos el Word
Wait Wind "Abriendo sesión Word" NoWait
oWord = CreateObject('word.application')

* si no se ha podido crear el objeto    
* salimos del método
If Type('oWord') # 'O'
   Return
EndIf	

* creamos documento nuevo
oWord.Documents.Add()
oWord.caption='Prueba OLE con Word97'

lnPagina = 1
For lnLine = 1 To oLista.ListCount
 If oLista.Selected(lnLine)
  Wait Wind "Generando documento:;
    Página "+allt(str(lnPagina))+;
    " de "+allt(str(lnNumPag)) NoWait
   If ThisForm.Combo1.DisplayValue = 1
**  creamos la cabecera de carta a 
**  clientes con los datos de las tablas
    Select customer
    Locate for ;
       cust_id=oLista.List(lnLine,1)
    lcCabecera = company + cr +;
		contact +cr +;
		address + cr+;
		postalcode + " " +;
		city+ cr+;		
		country		
    lcSaludo = "Estimado/a " +;
		 contact

    Else				
* Cabecera de la carta a empleados
     Select employee
     Locate for ;
	  emp_id = oLista.List(lnLine,1)
     lcCabecera = ALLT(First_name)+" "+;
   	ALLT(last_name)+ cr+;
 	Address + cr +;
	postalcode + " " +;
 	city + cr+;
	country
     lcSaludo = "Estimado/a"+first_name
    EndIf

* Insertar cabecera		
 oWord.Selection.InsertAfter(lcCabecera)

* Insertamos líneas en blanco y fecha
 lcEspacio = REPLICATE(cr,12)
 oWord.Selection.InsertAfter(lcEspacio)
 oWord.Selection.InsertAfter('Fecha: ')
 oWord.ActiveDocument.Bookmarks.add("fecha")
 lcEspacio = REPLICATE(cr,3)
 oWord.Selection.InsertAfter(lcEspacio)
 oWord.ActiveDocument.Bookmarks.add("saludo")
 oWord.Selection.InsertAfter(lcEspacio)
 oWord.ActiveDocument.Bookmarks.add("cuerpo")
 oWord.selection.Bookmarks('fecha').Select
 oWord.selection.InsertAfter(date())
 oWord.selection.Bookmarks('saludo').select
 oWord.selection.InsertAfter(lcSaludo+lcEspacio)
** Insertamos el texto de la carta que ;
   especificamos en el Editbox
 lcCuerpo = ThisForm.Edit1.Value
 oWord.selection.Bookmarks('cuerpo').select
 oWord.selection.InsertAfter(lcCuerpo+cr)
 oWord.Selection.InsertAfter(lcEspacio+lcEspacio)
 oWord.selection.InsertAfter('Un cordial ;
	saludo,'+cr)
 oWord.Selection.InsertAfter(lcEspacio)
 oWord.selection.InsertAfter(repl('_',25))
 oWord.Selection.InsertAfter(cr)
 oWord.selection.InsertAfter('Fdo.: Pepe;
	Pérez')
** Insertamos un salto de página si no ;
   estamos en la última página
  If lnPagina < lnNumPag
    oWord.Selection.InsertAfter(CHR(12))
  EndIf			
  Start=oWord.selection.Bookmarks('fecha').select
  End = oWord.ActiveDocument.;
	Bookmarks("\ENDOFDOC").select
  lnPagina = lnPagina +1
 EndIf
EndFor
* especificamos las dimensiones de la ;
  ventana word
oWord.top = 1
oWord.left = 1
oWord.width = 300		
oWord.height = 400		

MessageBox("Proceso concluido",64,"Aviso")
ThisForm.Refresh()

Si no tuviéramos el Word instalado, se produciría un error del tipo, no se ha encontrado la definición de la clase word.application” al intentar crear el objeto oWord. Este error lo procesamos en el evento Error del Botón cmdGenerar:

LPARAMETERS nError, cMethod, nLine
AERROR(laERROR)
cTextoError = "Error "+ALLT(STR(nError))+CHR(10)+laError[2]
MessageBox(cTextoError,16,"Atención")


 

Una vez concluido este proceso, habilitamos los botones de visualizar, imprimir, etc. Para ello basta hacer un refresh del formulario, ya que en el refresh de cada uno de estos botones hemos incluido la instrucción

This.Enabled = (Type('oWord') = 'O')

para que sólo estén activos si existe el objeto oWord. Mediante estos botones podemos visualizar el documento generado, imprimirlo, revisar su ortografía o salvarlo con estas sencillas órdenes:


**  Abrir ventana Preview
oword.visible= .t.
oWord.ActiveDocument.PrintPreview

**  Imprimir
oWord.ActiveDocument.PrintOut

**  Salvar
oWord.ActiveDocument.Save
(Salvar como sería 
 oWord.ActiveDocument.SaveAs(nombre.doc)
)
**  Ortografía
oWord.ActiveDocument.CheckSpelling

**  Cerrar documento y salir de Word
oWord.ActiveDocument.Close(0)		
* cerramos documento sin guardar cambios
oWord.Quit			
Release oWord
ThisForm.Refresh()

Este ejemplo es bastante sencillo con respecto a las posibilidades de programación que ofrece el Word, ya que el modelo de objetos del Word 97 incorpora aproximadamente 180 objetos. Para encontrar más información sobre este modelo, se puede consultar el tema Microsoft Word Visual Basic Reference en la ayuda del Word, siempre y cuando, eso sí, al instalar el Word, hallamos marcado la opción Online Help for Visual Basic en el checkbox correspondiente del setup. También hay bastante documentación en la web de Microsoft buscando la referencia Microsoft Word Objects en la sección Microsoft Office 97/Visual Basic Programmer’s Guide. Viendo la documentación que hay sobre este tema se aprecia claramente como todo esto está orientado principalmente a su uso desde el omnipresente Visual Basic (como no...), pero bueno, no hay que desanimarse, y con un poco de maña y algún retoque que otro, también los foxeros podemos aprovecharnos de ello.