Rellenar una plantilla de MS-Word a partir de una consulta en MS-Access

Hoja de texto

Se trata de realizar un informe de MS-Access utilizando MS-Word. Crearemos un nuevo documento de MS-Word con los datos provenientes de una tabla de MS-Access. Ofrecemos dos soluciones: la primera es una solución básica y la segunda solución es capaz de rellenar una tabla de detalles, o incluso varias tablas de detalles.

Ejemplo

Descarga

Está comprimido con 7-zip

Solución básica

  1. Creamos en MS-Word una plantilla, puede ser una carta, o un fax o un documento que luego vamos a imprimir en PDF. Para poner un campo ponemos el nombre del campo entre corchetes, por ejemplo [telefono]. Luego lo guardamos como plantilla (con extensión .dot) en la misma carpeta donde se encuentra nuestra base de datos.
  2. En MS-Access insertarmos en un módulo el código indicado en el apartado "Código fuente, ModuloInformeWord". De esta forma tendremos la función InformeWord lista para ser usada. Esta función tiene tres parámetros:
    ParámetroDescripciónEjemplo
    plantilla_wordEs el nombre del archivo de plantilla que hemos creado."informe_cliente.dot"
    consultaEs el nombre de una tabla o consulta. También puede ser una sentencia SQL."tabla_clientes"
    filtroSe utiliza para mostrar sólo un registro de la tabla o consulta."cliente_id=" & [cliente_id]
  3. En un formulario insertamos un botón y en su evento onClick (al hacer click) insertamos una llamada a la función:
    Al hacer clic:=InformeWord("informe_cliente.dot"; "tabla_clientes"; "cliente_id=" & [cliente_id])
    [cliente_id] es el nombre de un cuadro combinado independiente que se utiliza para seleccionar el cliente deseado.
  4. Al apretar el botón crea un documento Word nuevo basado en la plantilla que hemos creado con los campos entre corchetes sustituidos por los valores correspondientes de la tabla

Solución con tablas de detalles

  • Agregaremos el módulo de clase que se indica en el apartado "código fuente" para así disponer de la clase ClaseInformeWord
  • Según se muestra en la imagen de ejemplo la plantilla tiene un encabezado y un detalle. Los datos del encabezado provienen de la "tabla_clientes" y los datos del detalle provienen de la consulta "consulta_pedidos". El código a ejecutar para crear el informe a partir de la plantilla es el siguiente:
Private Sub ComandoInformePedidosClliente_Click()

Dim informe As New ClaseInformeWord
Dim filtro As String

filtro = "cliente_id=" & Me.cliente_id

Call informe.Abrir("informe_cliente_pedidos.dot")
Call informe.Ejecutar("tabla_clientes", filtro)
Call informe.EjecutarTablaDetalles(2, "consulta_pedidos", filtro)
Call informe.Cerrar

Set informe = Nothing
End Sub
En el ejemplo se supone que en el formulario hay un cuadro combinado independiente llamado "cliente_id" que se utiliza para realizar el filtro. También suponemos que la segunda tabla de la plantilla MS-Word contiene el detalle de los pedidos.

Métodos de la clase: ClaseInformeWord

  • Abrir - Inicia el proceso abriendo la plantilla de MS-Word
    ParámetroDescripciónEjemplo
    plantilla_wordNombre de la plantilla MS-Word. Se ha de encontrar en la misma carpeta que la base de datos"fax_cliente.dot"
  • Cerrar - Finaliza el proceso y muestra el documento creado
  • Ejecutar - Sustituye los campos entre corchetes de la plantilla por los valores del primer registro de la tabla o consulta indicados.
    ParámetroDescripciónEjemplo
    consultaNombre de la tabla o consulta de MS-Access"tabla_clientes"
    filtroFiltro aplicado sobre la tabla o consulta. Habitualmente se filtra sobre el campo ID clave principal"cliente_id=" & [cliente_id]
  • EjecutarTablaDetalles - Sustituye los campos entre corchetes de la tabla de detalles indicada en la plantilla MS-Word. Además añade tantas filas a dicha tabla como sean necesarias para mostrar todos los registros de la tabla o consulta indicados.
    ParámetroDescripciónEjemplo
    num_tablaSe utiliza para identificar la tabla de detalle de la plantilla MS-Word. Si la plantilla MS-Word tiene, por ejemplo, cinco tablas en total y la tabla de detalles es la tercera, entonces tendríamos que indicar un 3.3
    consultaNombre de la tabla o consulta de MS-Access"consulta_pedidos"
    filtroFiltro aplicado sobre la tabla o consulta. Habitualmente se filtra sobre el campo ID clave principal"cliente_id=" & [cliente_id]

Código fuente

"ModuloInformeWord"

Incluir el siguiente código en un nuevo módulo de la base de datos que se llame por ejemplo ModuloInformeWord:

'REFERENCIAS NECESARIAS:
'Menú -> Herramientas -> Referencias -> Microsoft Word Object Library

Public Function InformeWord( _
ByVal plantilla_word As String, _
ByVal consulta As String, _
Optional ByVal filtro As String = "" _
) As Boolean
On Error GoTo Errores
'Ejemplo de uso (evento al hacer clic de un botón de comando):
'=InformeWord("informe_cliente.dot";"tabla_clientes";"cliente_id=" & cliente_id)

Dim rs As DAO.Recordset
Dim campo As DAO.Field
Dim appWord As Word.Application
Dim documento_word As Word.Document
Dim ruta_actual As String

If filtro <> "" Then
consulta = "SELECT * FROM " & consulta & " WHERE " & filtro
End If
Set rs = CurrentDb.OpenRecordset(consulta, dbOpenForwardOnly)

If rs.BOF And rs.EOF Then
'Nada
Else

Set appWord = New Word.Application
appWord.Visible = False
Call SysCmd(acSysCmdInitMeter, "Exportando a Word", 100)
DoCmd.Hourglass True

If plantilla_word = "" Then
Set documento_word = appWord.Documents.Add()
Else
ruta_actual = Left(CurrentDb.Name, InStrRev(CurrentDb.Name, "\"))
Set documento_word = appWord.Documents.Add(ruta_actual & plantilla_word)
End If

For Each campo In rs.Fields

With appWord.Selection.Find
.ClearFormatting
.Text = "[" & UCase(campo.Name) & "]"
With .Replacement
.ClearFormatting
.Text = rs(campo.Name) & ""
End With
Call .Execute(Replace:=Word.WdReplace.wdReplaceAll)
End With

Next

End If
InformeWord = True
Salida:
On Error Resume Next
appWord.Visible = True
Call SysCmd(acSysCmdRemoveMeter)
DoCmd.Hourglass False
Set appWord = Nothing
Set documento_word = Nothing
rs.Close: Set rs = Nothing
Set campo = Nothing
Exit Function
Errores:
MsgBox Err.Description, vbCritical, "InformeWord"
Resume Salida
End Function

Módulo de clase "ClaseInformeWord"

Option Compare Database
Option Explicit

'REFERENCIAS NECESARIAS:
'Menú -> Herramientas -> Referencias -> Microsoft Word Object Library

'Ejemplo:
'' Dim informe As New ClaseInformeWord
'' Dim filtro As String
''
'' filtro = "cliente_id=" & Me.cliente_id
''
'' Call informe.Abrir("informe_cliente_pedidos.dot")
'' Call informe.Ejecutar("tabla_clientes", filtro)
'' Call informe.EjecutarTablaDetalles(2, "consulta_pedidos", filtro)
'' Call informe.Cerrar
''
'' Set informe = Nothing

Private app_word As Word.Application
Private documento_word As Word.Document

Private Sub Class_Initialize()
'Nada
End Sub

Private Sub Class_Terminate()
Call Cerrar
End Sub

Public Function Abrir(ByVal plantilla_word As String)
Dim ruta_actual As String

Set app_word = New Word.Application
app_word.Visible = False

If plantilla_word = "" Then
Set documento_word = app_word.Documents.Add()
Else
ruta_actual = Left(CurrentDb.Name, InStrRev(CurrentDb.Name, "\"))
Set documento_word = app_word.Documents.Add(ruta_actual & plantilla_word)
End If
End Function

Public Function Cerrar()
On Error Resume Next
app_word.Visible = True
Set app_word = Nothing
Set documento_word = Nothing
End Function

Public Function Ejecutar( _
ByVal consulta As String, _
Optional ByVal filtro As String = "" _
) As Boolean
On Error GoTo Errores
Call SysCmd(acSysCmdInitMeter, "Exportando a Word: " & consulta, 100)
DoCmd.Hourglass True

Dim rs As DAO.Recordset
Dim field As DAO.field

If filtro <> "" Then consulta = "SELECT * FROM " & consulta & " WHERE " & filtro
Set rs = CurrentDb.OpenRecordset(consulta, dbOpenForwardOnly)

If rs.BOF And rs.EOF Then
'Nada
Else
For Each field In rs.Fields
With app_word.Selection.Find
.ClearFormatting
.Text = "[" & UCase(field.Name) & "]"
With .Replacement
.ClearFormatting
.Text = rs(field.Name) & ""
End With
Call .Execute(Replace:=Word.WdReplace.wdReplaceAll)
End With
Next
End If
Ejecutar = True
Salida:
Call SysCmd(acSysCmdRemoveMeter)
DoCmd.Hourglass False
Exit Function
Errores:
MsgBox Err.Description, vbCritical, "Ejecutar"
Resume Salida
End Function

Public Function EjecutarTablaDetalles( _
ByVal num_tabla As Integer, _
ByVal consulta As String, _
Optional ByVal filtro As String = "" _
) As Boolean
On Error GoTo Errores
Call SysCmd(acSysCmdInitMeter, "Exportando a Word: " & consulta, 100)
DoCmd.Hourglass True

Dim rs As DAO.Recordset
Dim field As DAO.field
Dim tabla As Word.Table
Dim ultima_fila As Word.Row, nueva_fila As Word.Row
Dim celda As Word.Cell
Dim campo As String, valor As String

If filtro <> "" Then consulta = "SELECT * FROM " & consulta & " WHERE " & filtro
Set rs = CurrentDb.OpenRecordset(consulta, dbOpenForwardOnly)
Set tabla = documento_word.Tables(num_tabla)

If rs.BOF And rs.EOF Then
'Nada
Else
Do Until rs.EOF
Set ultima_fila = tabla.Rows(tabla.Rows.Count)
Set nueva_fila = tabla.Rows.Add
For Each celda In ultima_fila.Cells
'Duplicar la última fila en la nueva
campo = celda.Range.Text
campo = Left(campo, Len(campo) - 2) 'Eliminar vbCrLf del final
nueva_fila.Cells(celda.ColumnIndex).Range.Text = campo

'Poner los valores
For Each field In rs.Fields
If 0 <> InStr(LCase(field.Name), "importe") Then
valor = Format(Nz(rs(field.Name), 0), "#,##0.00")
Else
valor = rs(field.Name) & ""
End If
campo = Replace(campo, "[" & field.Name & "]", valor)
Next
celda.Range.Text = campo
Next

'Call SysCmd(acSysCmdUpdateMeter, rs.PercentPosition) 'Fallas porque es dbOpenForwardOnly
rs.MoveNext
Loop
End If

'Borrar la última fila
tabla.Rows(tabla.Rows.Count).Delete

EjecutarTablaDetalles = True
Salida:
Call SysCmd(acSysCmdRemoveMeter)
DoCmd.Hourglass False
Exit Function
Errores:
MsgBox Err.Description, vbCritical, "EjecutarTablaDetalles"
Resume Salida
End Function

Aviso: Para que funcione el módulo hay que incluir una referencia a la biblioteca de clases de Microsoft Word
Menú del módulo → Herramientas → Referencias → Microsoft Word Object Library

Comentarios

  1. 1 Jesús Huertas 2007-04-27 Hace 11 años
    Hola a todos,

    Quería plantearos varias dudas sobre este ejercicio. En la solución básica, me da un error de que falta operadores, pero primero os cuento yo pretendo utilizar como consulta una consulta de parametros y que el parametro metido funcione como filtro. El campo que yo utilizo es un campo numérico.

    Llevo dos días dandole vueltas al tema, de como poner un botón en un formulario que me abra una consulta de parametro yo introduzco un codigo numerico y se me abre un documento en word con los datos vinculados a cada codigo.

    Espero sugerencias
  2. 2 Pablo Moraiz 2007-09-20 Hace 10 años
    Hola esta excelente tu material, pero me han surgido algunos problemas, seria mucho pedir si colocaras el ejemplo en forma completa para poder bajarlo, como para tener a modo de ejemplo y asi poder chequear en que tamos haciendo mal, xq hasta ahora no he podido salir del pantana, con algo q estoy haciendo y que quiero mostrar impreso, desde ya agradesco la ayuda q me has brindado igual sigo investigando. Saludos.
  3. 3 Sol 2007-10-05 Hace 10 años
    Al momento he revisado el código de tu ejercicio y me parece genial pero al momento de querer aplicarlo a mi necesidad me salta el error 3464 que se refiere a que los campos no coinciden en el tipo, en este caso he pensado que tal vez mis tablas son demasiado grandes, sin embargo no puedo omitir ningún campo de ellas. Has tenido este problema? quisa me puedas dar luces para superar el inconveniente.De antemano muchas gracias por tu ayuda.Saludos
  4. 4 drako 2009-04-15 Hace 9 años
    Me parecio muy interesante tu propuesta
    pero al parecer tengo un error o mas bien una duda si es te codigo es general para cualkier BD al q se lo implemente o simplemente tienen q tener la misma estructura q tu manejas en tu BD ejemplo si es asi podrias darme una ayudadita con siertas partes del codigo para q pueda amoldarlo a mis necesidades
    de antema gracias !!!!
  5. 5 Ignasi 2009-05-20 Hace 9 años
    Buenas tardes,

    He utilizado tu ejemplo y funciona perfectamente! Muchas gracias por compartirlo.

    Ahora tengo una pregunta... Es posible adaptar este mismo procedimiento para Excel?

    Seria posible?

    Muchas gracias!
  6. 6 RAMON 2009-09-30 Hace 8 años
    Gracias por este material.
    Funciona perfectamente pero tengo un pequeño problema:

    Trabajando con campos memo, cuando se excede de los 255 carácteres salta el error "El parámetro de la cadene es demasiado largo".

    ¿Se puede solucionar de alguna forma?

    Gracias
  7. 7 Francisco 2009-09-30 Hace 8 años
    Tienes razón Ramón.

    Si el campo es muy largo no funciona. Hay otro método alternativo que se podría seguir y sería mediante marcadores de Word. Pero este código no lo tengo y no sé si tendría el mismo problema.

    Saludos
  8. 8 Tatiana 2010-07-13 Hace 7 años
    Hola,

    estoy intentando hacer el ejemplo de modulo informe word pero no consigo que detecte los campos entre corchetes en la plantilla word. De hecho me hace un fallo que me cierra el access.
    Pienso que a lo mejor es porque están dentro de tablas, pero estoy empezando con VB y no sé cómo modificar el ejemplo para que lea el contenido de estas tablas

    ¿Como sería la solución?
  9. 9 xavi 2010-09-27 Hace 7 años
    Muy buen ejemplo gracias.

    Quisiera aprovechar para preguntarte en el caso de los encabezados y pies de página no funcionan los campos.

    Hay alguna forma de darle formato al campo a mostrar en Word, es decir una fecha larga qe salga como tal. O un campo numerico que salga 00001 en vez de 1


    Saludos y gracias de antemano
  10. 10 Fernando 2010-10-19 Hace 7 años
    Si utilizas como origen una consulta puedes añadir un campo nuevo con esto:
    FECHALARGA: Día([FECHA]) & " de " & NombreMes(Mes([FECHA])) & " de " & Año([FECHA])

    En la plantilla de word utilizarias el campo FECHALARGA como origen.
  11. 11 xavi 2010-10-24 Hace 7 años
    Muchas gracias, me ha servido (la verdad es que no lo habia pensado)

    Una cosa el informe por tablas. Tiene algún limite. Es que estoy provando con un informe que tiene muchas tablas que llaman a muchas consultas y me sale el siguiente error. "El elemento del conjunto solicitado no existe". Si hago lo mismo en una formulario de word con una sola tabla me sale perfecto, pero cuando tengo 8 o 9 tablas en el mismo informe me suele dar este error.

    Saludos
  12. 12 gely 2010-10-28 Hace 7 años
    Muchas Gracias por el ejemplo funciona perfectamente , pero soy incapaz de hacerlo funcionar cuando quiero que me combine todos los registros o bien existes mas registros que cumplen la condicion del filtro, podrias ayudarme.

    Gracias
  13. 13 Fer 2010-12-02 Hace 7 años
    Hola gely, no te acabo de entender muy bien lo que quieres, pero te cuento ( más o menos según lo que he interpretado).

    Si quieres usar todos los registros de una tabla lo que debes hacer es suprimir en el código la línea que "declara" el filtro:
    filtro = "cliente_id=" & Me.cliente_id
    y luego pones como origen la tabla o consulta que sea (entiendo que quieres que los registros te salgan seguidos, en la primera tabla de la plantilla de word):
    Call informe.EjecutarTablaDetalles(1, "TABLADATOS", filtro)

    La otra parte no entiendo muy bien lo que quieres, así que no te puedo ayudar del todo. Si utilizas como origen de datos la consulta intenta "afinarla" para seleccionar los que te interesen, o si por el contrario buscas que te fusione con el registro que estas trabajando tienes que tener un campo que identifique a ese expediente (ideal: un campo "ID" autonumérico).
  14. 14 Fer 2010-12-02 Hace 7 años
    Xavi al utilizar tablas hay que tener cuidado (te cuento lo que me ha pasado a mí) ya que puedes equivocarte en varias cosas:
    - El número de la tabla, pudiendo incluso repetirlo.
    - El origen de los datos de cada tabla
    - Los campos que pones en la tabla no están en el origen que has puesto en el código.

    En mi caso concreto he utilizado 9 tablas sin problema (después de ir puliendolo, jeje).

    Yo te aconsejería que imprimieras tal cual la plantilla de word por un lado, el código asignando los orígenes de datos y luego fueras comprobando que están las cosas bien puestas.

    Sl2
  15. 15 jose 2011-12-01 Hace 6 años
    Hola, es un trabajo muy bueno para combinación con Word, me funciona perfecto, pero como comentaba xavi, en lo encabezados y pies de pagina no sustituye el texto por los datos de la consulta, alguien puede darme una orientación.
    Muchas gracias.
  16. 16 SHIRLEY GIRALDO 2012-01-03 Hace 6 años
    hola esta muy bueno tu ejemplo, pero tengo un problema.. al ejecutarlo independiente me funciona perfectamente..
    pero al incorporarlo con otra base de datos en access 2010 al generar la plantilla me sale error : no coinciden los tipos

    SI ME PUDIERAS COLABORAR GRACIAS HE VISTO POR TODO LADO Y NO ENCUENTRO
  17. 17 SHIRLEY GIRALDO 2012-01-13 Hace 6 años
    hola, estoy intentanto anexar tu base de datos a otra que tengo en acces 2010 y me sale un error de ## no coinciden los tipos ## y no he podido solucionarlo, creo que es por algun evento o codigo de visual pero no lo he solucionado,,
  18. 18 Francisco 2012-01-14 Hace 6 años
    Shirley Giraldo, he creado una versión de la base de datos a descargar para Microsoft Office 2007. Prúeba con esta otra versión a ver que tal.
  19. 19 Jacobo 2012-03-15 Hace 6 años
    Hola Francisco,

    Gracias por este trabajo, es muy útil. Me gustaría saber si existe solución para lo que ya han comentado antes, que los campos situados en el encabezado o el pie de la plantilla de Word no se reemplazan por los valores.
  20. 20 bombates 2012-04-23 Hace 6 años
    Hola muy buenas :)

    Ante todo, gracias por tu trabajo, funciona a las mil maravillas.

    Solo tengo un problema, cuando se trata de combinar un campo calculado no funciona. Me combina todo menos ese tipo de campo.

    Cuando pincho en el boton me aparece el tipico error de "No coinciden los campos", pincho en aceptar y me abre el Word con todos los campos correctos execepto el campo calculado. ¿Habria alguna manera de solucionarlo?

    Estoy consultando en mas sitio, pero solo llevo 3 meses trabajando con Acces y de programacion no tengo ni idea.

    ¿Podrias ayudarme? Aunque sea decirme por donde empezar para lograr solucionarlo.

    Muchas gracias y un saludo.
  21. 21 olimpo cardenas 2013-04-02 Hace 5 años
    lo estoy intentando montar a una bd q tengo y me sale este error cuando tiro el informe por cosulta error de compilacion:
    No se ha definido el tipo definido por el usuario
  22. 22 olimpo cardenas 2013-04-15 Hace 5 años
    hola ya solucione algo q te había preguntado la vez pasada ahora el inconvenientes es q en vez de 1 cuadro combinado necesito 2 como para filtrar por fecha y cliente y lo otro es q cuando me exporta me sale no coinciden los tipos pero igual me exporta la información gracias espero tu pronta respuesta
  23. 23 Javier 2013-12-04 Hace 4 años
    En el código del modulo de clase a la hora de ejecutar la tabla de detalles agrega las filas necesarias para los registros filtrados, pero en cada fila de la tabla me repite siempre los mismos datos, en mi caso los últimos que aparecen filtrados en la consulta.
    Mis datos son de tipo texto. Alguien me podría ayudar para saber donde tengo que modificar el código para que en cada fila me ponga los datos correspondientes a cada registro.
    Gracias por anticipado.
  24. 24 Raúl 2013-12-08 Hace 4 años
    Hola,

    El script es perfecto y funciona bien incluso en Access 2013. El único problema que he encontrado es el que surge cuando la consulta incluye campos de tipo "datos adjuntos" ¿cómo podría tratar estos campos para que se incorporasen al Word como si los hubiese insertado? Gracias por todo
  25. 25 sergio 2014-01-17 Hace 4 años
    Muchas gracias. Lo uso y me funciona perfectamente y me es muy util, pero tengo una pregunta a ver si me la puedes resolver.

    Para que em funcione la plantilla de word tiene que estar en la misma carpeta que la base de datos. Pero yo trabajo teniendo los datos en un ordenador en red y en el puesto tengo una base de datos con tablas vinculadas. Me gustaria poder dejar las plantillas en la carpeta del ordenador que esta en red y que cuando ejecute el programa para rellenar la plantilla de word tire desde esa carpeta en vez de la carpeta desde donde estoy ejecutando la base de datos. ¿Es posible?¿Como? Muchas gracias.
  26. 26 Emilio 2014-05-25 Hace 3 años
    Muchas gracias por el aporte. Funciona perfectamente y me ha servido para solucionar problemas de modelos de informes que algunos incompetentes publican en los Boletines Oficiales.

  27. 27 Ivo 2017-02-16 Hace 9 meses
    Hola.
    Te agradezco el aporte. Muy útil, he utilizado ambas y funciona a la perfección (hasta 8 tablas).

    Lo que necesito hacer es generar un único documento para muchas cabeceras. Siguiendo tu ejemplo, necesito generar un unico documento para todos los clientes.
    Lo estoy utilizando para generar y mantener un catalogo de servicios. Entonces por cada servicio tengo detalles. Todos deben quedar en el mismo documento. Hasta ahora genero sin problemas para un servicio.

    Muchas gracias

Proinf.net, ©2003-2017 ci 3.1.6 (CC) Esta obra está bajo una licencia de Creative Commons Este software está sujeto a la CC-GNU GPL