21 de octubre de 2003

Combinar correspondencia con Word desde Visual FoxPro

En este ejemplo vamos realizar la tarea de Combinar Correspondencia con Microsoft Word desde Visual FoxPro. Para ello vamos a crear el documento y vamos a utilizar la herramienta "Mail Merge" de Word. Los datos a combinar los tomaremos de la base de datos "Northwind" que viene en los ejemplos de Visual FoxPro 8.

Definición de la clase cWord

Para combinar correspondencia definimos de una clase llamada cWord con los métodos necesarios para esta tarea. El código de la clase es el siguiente:
*--------------------------------------------------
* Clase cWord
*--------------------------------------------------
DEFINE CLASS cWord AS CUSTOM
  *--
  * Propiedades
  *--
  oWord = .NULL.   &&   Objeto Word
  cDirApp = ADDBS(SYS(5) + SYS(2003))
  cDirDat = ADDBS(HOME(2) + 'Northwind')
  cDataSource = ''
  *--------------------------------------------------
  * Creo el servidor de automatización
  *--------------------------------------------------
  PROCEDURE CrearServidor()
    *-- Creo el objeto
    THIS.oWord = CREATEOBJECT('Word.Application')
    RETURN VARTYPE(THIS.oWord) = 'O'
  ENDPROC
  *--------------------------------------------------
  * Cierro el servidor de automatización
  *--------------------------------------------------
  PROCEDURE CerrarServidor()
    *-- Cierro Word
    THIS.oWord.QUIT()
    THIS.oWord = .NULL.
    RETURN
  ENDPROC
  *--------------------------------------------------
  * Abro la Carta, si no existe la creo
  *--------------------------------------------------
  PROCEDURE AbrirCarta(tcArchivo)
    LOCAL loDoc AS 'Word.Document'
    tcArchivo = FORCEEXT(tcArchivo,'DOC')
    IF NOT FILE(THIS.cDirApp + tcArchivo)
      *-- Si no existe la carta, la creo
      loDoc = THIS.CrearCarta(tcArchivo)
    ELSE
      *-- Si existe la carta, la abro
      loDoc = THIS.oWord.Documents.OPEN(THIS.cDirApp + tcArchivo)
      *-- y me aseguro que no tiene un documento asociado
      loDoc.MailMerge.MainDocumentType = -1  && wdNotAMergeDocument
    ENDIF
    *-- Retorno un objeto Document
    RETURN loDoc
  ENDPROC
  *--------------------------------------------------
  * Creo la Carta
  *--------------------------------------------------
  PROCEDURE CrearCarta(tcArchivo)
    LOCAL loDoc AS 'Word.Document'
    *-- Creo un nuevo documento
    loDoc = THIS.oWord.Documents.ADD(,,0)
    *-- Guardo el documento como ...
    loDoc.SAVEAS(THIS.cDirApp + tcArchivo)
    *-- Activo el documento
    loDoc.ACTIVATE
    *-- Comienzo a 'escribir' el documento
    WITH THIS.oWord.SELECTION
      .FONT.NAME = 'Tahoma'
      .FONT.SIZE = 10
      .ParagraphFormat.ALIGNMENT = 2 && wdAlignParagraphRight
      .TypeText('San Miguel de Tucumán, ' + DTOC(DATE()))
      .TypeParagraph
      .ParagraphFormat.ALIGNMENT = 0 && wdAlignParagraphLeft
      .TypeParagraph
      .TypeParagraph
      .TypeParagraph
      .TypeParagraph
      .TypeParagraph
      .TypeParagraph
      .TypeText('Señores: ')
      .FONT.Bold = .T.
      .FIELDS.ADD(.RANGE, -1, 'MERGEFIELD  CompanyName ')
      .FONT.Bold = .F.
      .TypeParagraph
      .TypeText('At: ')
      .FIELDS.ADD(.RANGE, -1, 'MERGEFIELD  ContactName ')
      .TypeParagraph
      .FIELDS.ADD(.RANGE, -1, 'MERGEFIELD  Address ')
      .TypeParagraph
      .FIELDS.ADD(.RANGE, -1, 'MERGEFIELD  PostalCode')
      .TypeText(' - ')
      .FIELDS.ADD(.RANGE, -1, 'MERGEFIELD  City ')
      .TypeParagraph
      .FONT.Underline = 1  && wdUnderlineSingle
      .FIELDS.ADD(.RANGE, -1, 'MERGEFIELD  Country ')
      .FONT.Underline = 0  && wdUnderlineSingle
      .TypeParagraph
      .TypeParagraph
      .TypeParagraph
      .TypeParagraph
      .TypeText(CHR(9) + 'Estimado/a ')
      .FIELDS.ADD(.RANGE, -1, 'MERGEFIELD  ContactName ')
      .TypeParagraph
      .TypeParagraph
      .TypeText(CHR(9) + 'Nos dirigimos a Ud. con el objeto de comunicarle ' + ;
        'la nueva dirección de nuestra empresa')
      .TypeParagraph
      .TypeParagraph
      .FONT.Bold = .T.
      .TypeText('Informática del Tucumán')
      .FONT.Bold = .F.
      .TypeParagraph
      .TypeText('9 de Julio 123, 1° Piso')
      .TypeParagraph
      .TypeText('4000 - San Miguel de Tucumán')
      .TypeParagraph
      .TypeText('Tucumán, Argentina')
      .TypeParagraph
      .TypeText('Teléfono (+54) (381) 681-4521')
      .TypeParagraph
      .TypeParagraph
      .TypeText(CHR(9) + 'Sin otro particular lo saludamos atte.')
      .TypeParagraph
      .TypeParagraph
      .TypeParagraph
      .TypeParagraph
      .TypeParagraph
      .TypeParagraph
      .TypeText('Manuel Belgrano')
      .TypeParagraph
      .TypeText('Socio Gerente')
      .TypeParagraph
      .TypeText('Informática del Tucumán')
      .TypeParagraph
    ENDWITH
    *-- Retorno un objeto Document
    RETURN loDoc
  ENDPROC
  *--------------------------------------------------
  * Creo archivo DataSource
  *--------------------------------------------------
  PROCEDURE GenerarDataSource
    LOCAL lcArchivo AS CHARACTER
    IF NOT DBUSED('Northwind')
      OPEN DATABASE (THIS.cDirDat + 'Northwind') SHARED
    ENDIF
    SET DATABASE TO 'Northwind'
    *-- Consulta a los Clientes de Northwind
    SELECT CompanyName, ContactName, ;
      Address, PostalCode, City, Country ;
      FROM Customers ;
      INTO CURSOR Clientes
    *-- Copio el cursor al archivo para combinar
    lcArchivo = SYS(2015)
    COPY TO (THIS.cDirApp + lcArchivo) TYPE CSV
    USE IN Clientes
    THIS.cDataSource = THIS.cDirApp + FORCEEXT(lcArchivo,'CSV')
    RETURN
  ENDPROC
  *--------------------------------------------------
  * Combino la Carta
  *--------------------------------------------------
  PROCEDURE CombinarCarta(toDoc)
    WITH toDoc.MailMerge
      .MainDocumentType = 0  && wdFormLetters
      .OpenDataSource(THIS.cDataSource)
      .Execute()
    ENDWITH
    WITH THIS.oWord
      *-- Cambio la Carpeta del cuadro de diálogo 'Guardar...'
      .ChangeFileOpenDirectory(THIS.cDirApp)
      *-- Maximizo y hago visible
      .WINDOWSTATE = 1  && wdWindowStateMaximize
      .VISIBLE = .T.  && True
    ENDWITH
    RETURN
  ENDPROC
  *--------------------------------------------------
  * Guardo el Documento, si tlCierra = .T. lo cierra
  *--------------------------------------------------
  PROCEDURE GuardarCarta(toDoc, tlCierra)
    *-- Guardo el documento
    toDoc.SAVE()
    IF tlCierra
      *-- Cierro el documento
      toDoc.CLOSE()
    ENDIF
  ENDPROC
ENDDEFINE
*--------------------------------------------------
* Fin Clase cWord
*--------------------------------------------------
El programa Combinar.prg

El siguiente es el código de nuestro programa "Combinar.prg" donde creamos una instancia de la clase cWord y comenzamos a ejecutar los métodos de esa clase.
*-------------------------------------------------
* Combinar.prg
*-------------------------------------------------
LOCAL lo AS OBJECT, loDoc AS OBJECT
lo = NEWOBJECT('cWord','cWord.prg')
IF lo.CrearServidor()
   *-- Ejecuto métodos de la clase
   loDoc = lo.AbrirCarta('MiCarta')
   lo.GenerarDataSource()
   lo.CombinarCarta(loDoc)
   lo.GuardarCarta(loDoc, .T.)
ELSE
   MESSAGEBOX('No se pudo instanciar el servidor', 16, 'Error!')
ENDIF
lo = .NULL.
RETURN
*-------------------------------------------------
* Fin Combinar.prg
*-------------------------------------------------
Ejecutando los métodos en la clase cWord

En primer lugar invocamos el método CrearServidor() que nos retorna el objeto oWord que será nuestro servidor de automatización.

Abrir y/o crear la carta

El método AbrirCarta(), abre la carta de Word si esta existe o crea una nueva carta con el método CrearCarta(). Ambos métodos retornan un objeto Document de Word.


Crear la carta

En el caso de crear o abrir una carta ya existente de Word, estas deben contener los nombres de los campos para su reemplazo en la combinación. Estas cartas serán los documentos principales para la combinación.

La fuente de los datos

También debemos crear o abrir los documentos con los datos a combinar. En este ejemplo creamos un archivo del tipo CSV (Valores Separados por Comas) desde una cláusula SELECT a la tabla "Customers" de la base de datos "Northwind"


Obtener los datos a combinar


Crear la fuente de datos (archivo .CSV)

Para esta tarea tenemos el método GenerarDataSource() que crea el archivo con los datos y establece la propiedad cDataSource de la clase cWord.

Combinar la carta

En el método CombinarCarta() ejecutamos las siguientes sentencias para:
  • Hacer la carta del tipo Documento Principal.
  • Abrir el archivo con la fuente de datos.
  • Ejecutar la combinación

Ejecutar la combinación

Guardar la carta

Para finalizar tenemos el método GuardarCarta() que guarda el documento principal, con la posibilidad mediante un parámetro de cerrar el documento.

En este ejemplo el documento combinado que se genera quedará abierto, entonces hacemos la aplicación visible para que el usuario lo guarde o imprima directamente desde la ventana de Word. También establecemos la carpeta donde están los documentos de este ejemplo, para que Word por defecto la seleccione en la ventana de "Guardar...".

Pueden descargar el código fuente de los programas desde el siguiente vínculo: combinar.zip

Hasta la próxima.

Luis María Guayán

3 comentarios :

  1. aún no lo pruebo pero creo no tener problema para implementarlo. Recién haré un pequeño software de control de obra pública para contratistas y creo que visual FoxPro 9 es una buena alternativa para ello, ya que será un sistema de escritorio. De antemano muchas gracias por tu aportación. Atentamente Juan Carlos Ojeda

    ResponderBorrar
  2. TENGO UN ARCHIVO YA DISEÑADO EN WORD, AL CUAL SOLO REQUIERO PASAR ALGUNOS DATOS DE UNA TABLA DE VISUAL FOX, TIPO COMBINAR CORRESPONDENCIA, POR METODO DE REEMPLAZO, ASI EVITO REDISEÑAR DE NUEVO EL DOCUMENTO Y SOLO ACTUALIZO LOS DATOS DESDE VISUAL FOX. HABRÁ UNA MANERA DE AUTOMATIZARLO. ALGUIEN ME PUEDE AYUDAR. MI CORREO ES f3rcho68@icloud.com

    ResponderBorrar
  3. Hice todo el procedimiento, pero en
    WITH toDoc.MailMerge
    .MainDocumentType = 0 && wdFormLetters
    .OpenDataSource(THIS.cDataSource)
    .Execute()
    ENDWITH
    no me reconoce el cDataSource, y me saca el formulario sin reemplazar los campos
    O sea saca el formulario original con los nombres de las variables.

    ResponderBorrar

Los comentarios son moderados, por lo que pueden demorar varias horas para su publicación.