2

I am totally new to VBA and I am writing a code to mail merge data from each row in an excel sheet to a certain word document and save that document with name corresponding to the first cell value from each row.

Each row contains the information of a client. That is why I have to mailmerge each row info seperately.

So far the code works fine, but two problems I need to solve:

1) SQLStatement:="SELECT * FROMSheet1$" ends up mail merging info from all the rows in sheet during each iteration of the for loop (the loop iterates through each row). So what happens is that, each client's document includes data of other clients (excel rows) as well.

2) The usual automation error unless I keep the source word document open.

So can someone please tell me how to select the info from only the row where the iteration has reached.

I triedSQLStatement:="SELECT rw.row* FROMSheet1$" But it does not work

Any help would be good. The full code is:

Sub RunMerge()

'booking document begins here

Dim wd As Object
Dim wdocSource As Object
Dim activedoc
Dim strWorkbookName As String
Dim x As Integer
Dim cdir As String
Dim client As String

Dim sh As Worksheet
Dim rw As Range
Dim rowcount As Integer

Set sh = ActiveSheet
For Each rw In sh.Rows
    If sh.Cells(rw.Row, 1).Value = "" Then
        Exit For
    End If



cdir = "C:\Users\Kamlesh\Desktop\"
client = Sheets("Sheet1").Cells(rw.Row + 1, 1).Value
Dim newname As String
newname = "Offer Letter - " & client & ".docx"


On Error Resume Next
Set wd = GetObject(, "Word.Application")
If wd Is Nothing Then
    Set wd = CreateObject("Word.Application")
End If
On Error GoTo 0
Const wdFormLetters = 0, wdOpenFormatAuto = 0
Const wdSendToNewDocument = 0, wdDefaultFirstRecord = 1, wdDefaultLastRecord = -16

Set wdocSource = wd.Documents.Open("C:\Users\Kamlesh\Desktop\master\Regen-booking.docx")

strWorkbookName = ThisWorkbook.Path & "\" & ThisWorkbook.Name

wdocSource.MailMerge.MainDocumentType = wdFormLetters

wdocSource.MailMerge.OpenDataSource _
        Name:=strWorkbookName, _
        AddToRecentFiles:=False, _
        Revert:=False, _
        Format:=wdOpenFormatAuto, _
        Connection:="Data Source=" & strWorkbookName & ";Mode=Read", _
        SQLStatement:="SELECT * FROM `Sheet1$`"

With wdocSource.MailMerge
    .Destination = wdSendToNewDocument
    .SuppressBlankLines = True
    With .DataSource
        .FirstRecord = wdDefaultFirstRecord
        .LastRecord = wdDefaultLastRecord
    End With
    .Execute Pause:=False
End With

wd.Visible = True
wd.ActiveDocument.SaveAs cdir + newname
'wdocSource.Close SaveChanges:=False
'wd.Quit
Set wdocSource = Nothing
Set wd = Nothing


Next rw

End Sub

My Excel Sheet looks like this

enter image description here

Community
  • 1
  • 1
Rahul Ramesh
  • 25
  • 1
  • 4
  • BTW why are you creating and destroying the object in a loop, that that part outside – Siddharth Rout Aug 05 '16 at 05:34
  • ohh, Ya just getting used to this VBA, Since this is an assignment am honestly trying to get the output. Started with learning VBA day before yesterday. Please do give some details about your advise. It would be so so helpful. Thanks – Rahul Ramesh Aug 05 '16 at 05:44
  • `Started with learning VBA day before yesterday`? :) And if you have written this code then it is really commendable :) – Siddharth Rout Aug 05 '16 at 05:46
  • lol, not commendable. Nightmare and starvation to meet the deadline :'( Also lots of help from internet as well. – Rahul Ramesh Aug 05 '16 at 05:51

1 Answers1

1

Try this. Obviously this is untested as I do not know your header names and values

SQLStatement:="SELECT * FROM `Sheet1$` WHERE SomeField = 'SomeUniqueValue'"

Something like

SQLStatement:="SELECT * FROM `Sheet1$` WHERE Client = " & Range("A" & rw + 1).Value & "'"
  1. Replace "A" by the actual column
  2. Replace "Client" by the actual header of the column

Also like I mentioned in the comment below the question, why are you creating and destroying objects in the loop? You can instantiate the Word Application out of the For loop. And you can destroy it out of the For Loop.

Is this what you are trying? (UNTESTED)

Change sSQL = "SELECT * FROMSheet1$WHERE [Client Name] = '" & .Range("A" & i).Value & "'" in the below code as per your requirements.

Const wdFormLetters = 0, wdOpenFormatAuto = 0
Const wdSendToNewDocument = 0, wdDefaultFirstRecord = 1, wdDefaultLastRecord = -16

Sub RunMerge()
    Dim wd As Object, wdocSource As Object
    Dim sh As Worksheet
    Dim Lrow As Long, i As Long
    Dim cdir As String, client As String, newname As String
    Dim sSQL As String

    cdir = "C:\Users\Kamlesh\Desktop\"

    On Error Resume Next
    Set wd = GetObject(, "Word.Application")
    If wd Is Nothing Then
        Set wd = CreateObject("Word.Application")
    End If
    On Error GoTo 0

    Set wdocSource = wd.Documents.Open(cdir & "\master\Regen-booking.docx")
    Set sh = ActiveSheet
    strWorkbookName = ThisWorkbook.Path & "\" & ThisWorkbook.Name

    With sh
        Lrow = .Range("A" & .Rows.Count).End(xlUp).Row

        For i = 2 To Lrow
            If Len(Trim(.Range("A" & i).Value)) <> 0 Then
                client = .Cells(i, 1).Value
                newname = "Offer Letter - " & client & ".docx"

                wdocSource.MailMerge.MainDocumentType = wdFormLetters

                '~~> Sample String
                sSQL = "SELECT * FROM `Sheet1$` WHERE [Client Name] = '" & .Range("A" & i).Value & "'"

                wdocSource.MailMerge.OpenDataSource Name:=strWorkbookName, _
                AddToRecentFiles:=False, Revert:=False, Format:=wdOpenFormatAuto, _
                Connection:="Data Source=" & strWorkbookName & ";Mode=Read", _
                SQLStatement:=sSQL

                With wdocSource.MailMerge
                    .Destination = wdSendToNewDocument
                    .SuppressBlankLines = True
                    With .DataSource
                        .FirstRecord = wdDefaultFirstRecord
                        .LastRecord = wdDefaultLastRecord
                    End With
                    .Execute Pause:=False
                End With

                wd.ActiveDocument.SaveAs cdir & newname
                wd.ActiveDocument.Close SaveChanges:=False
            End If
        Next i
    End With

    wdocSource.Close SaveChanges:=False
    wd.Quit

    Set wdocSource = Nothing
    Set wd = Nothing
End Sub
Siddharth Rout
  • 147,039
  • 17
  • 206
  • 250
  • SQLStatement:="SELECT * FROM `Sheet1$` WHERE SomeField = 'rw.row+1`" like this? Sorry I am very new to VBA :( – Rahul Ramesh Aug 05 '16 at 05:34
  • It returned type mismatch error. Also one issue, why is "Client" coming up, the program is suppose to select all the info from the row under iteration, :( – Rahul Ramesh Aug 05 '16 at 05:55
  • Can you post a screenshot of your data? – Siddharth Rout Aug 05 '16 at 06:06
  • I tried running it, no response from excel. And after some time a pop up window came with "select table" as option. That was also not responding. Let me ask. like usually in VBA how do u select only contents from one single row? Like if i want to select all the values from, say row1, how do u do it using the statement: SQLStatement:="SELECT * FROM Sheet1$ – Rahul Ramesh Aug 05 '16 at 06:14
  • that is what my sql string does... go through my post once again. you have to replace "client" with the relevant header name. – Siddharth Rout Aug 05 '16 at 06:15
  • the data is: http://imgur.com/a/O8RWl It is basically a customer form, already all fields have been mail merged from the excel sheet. So basically, when the macro runs it is just suppose to automate and produce the customer form for different customers and store them onto desktop – Rahul Ramesh Aug 05 '16 at 06:17
  • It worked!!!!. How, I mean what was wrong in my program (obviously, mostly everything), can you gimme some pointers?? Your program worked without any issue. :D – Rahul Ramesh Aug 05 '16 at 06:31
  • Also, I have two more documents to follow the same procedure from the same excel sheet, shall I put those two documents also with different variable names into yours? That is a rudimentary way, but will it also work? – Rahul Ramesh Aug 05 '16 at 06:32
  • See the points 1 and 2 in my above post and then compare it with my code ;) Your column header ws "Client Name" and not "Client" – Siddharth Rout Aug 05 '16 at 06:33
  • Also see how I am creating and destroying objects outside the loop. I would recommend spending some time with the code to understand what it does :) – Siddharth Rout Aug 05 '16 at 06:36
  • Sure, i will. Thank you a lot. I only knew python and a little php, this visual basic thing seems to have a lot of use in real world as well. I will look more into it from now on. Thank you. Also "automation error" still happens, even with your new code, but I guess that is a small issue I can solve by keeping the source word document open always – Rahul Ramesh Aug 05 '16 at 06:43
  • `wdocSource` is always open in my code. It is closed at the end. – Siddharth Rout Aug 05 '16 at 06:45
  • yes. I can fix that. Just one more question, if possible can you tell me, to create a new folder (having the client's name) in the cdir (that being desktop) path in the above program is the following command correct: newdir = Mkdir cdir + client – Rahul Ramesh Aug 05 '16 at 06:56
  • Try it and tell me ;) – Siddharth Rout Aug 05 '16 at 07:00
  • If Dir(cdir & "\" & client, vbDirectory) = "" Then Mkdir cdir + client EndIf It seems to output some error – Rahul Ramesh Aug 05 '16 at 07:21
  • `If Dir(cdir & client, vbDirectory) = "" Then Mkdir cdir & client` You already have a "\" in `cdir` – Siddharth Rout Aug 05 '16 at 07:24
  • Yes, that is fixed. Now the only issue is in the final statement wd.ActiveDocument.SaveAs cdir & client & "\" & newname....... In this they showcase an error "Object not defined" – Rahul Ramesh Aug 05 '16 at 07:36