Vba Macros not working correctly - vba

I have an Excel file with employee data and a Word template. I have mail merged and created a macro which asks me for the employee name, and based on the name, it fetches the employee details of that employee to the Word file from the Excel file.
The macro is able to retrieve records if the record comes from top to bottom of the Excel sheet. However, any record which is above the current retrieved record is not getting fetched. It's really a trouble. Could you please help?
Below is my code:
Sub getdata()
Dim numRecord As Integer
Dim myName As String
myName = InputBox("Enter Name:")
Set dsMain = ActiveDocument.MailMerge.DataSource
If dsMain.FindRecord(FindText:=myName, Field:="Name") = True Then
numRecord = dsMain.ActiveRecord
End If
End Sub

Here is a direct quote from the MailMergeDataSource.FindRecord Method documentation:
"The FindRecord method does a forward search only. Therefore, if the active record is not the first record in the data source and the record for which you are searching is before the active record, the FindRecord method will return no results. To ensure that the entire data source is searched, set the ActiveRecord property to wdFirstRecord ."
This, according to the MailMergeDataSource.ActiveRecord Property documentation will be something like:
With ActiveDocument.MailMerge
.DataSource.ActiveRecord = wdFirstRecord
End With

Related

Reading a MailMerge field from a Word document

I am trying to write a VBA script that will read the value of a merge field in an MS Word document. This field's code is:
{MERGEFIELD Vendor_ID \* MERGEFORMAT}
I have tried accessing it via MailMergeField:
' No access by ID, must use an index:
ActiveDocument.MailMerge.Fields(1)
but this object does not seem to provide the value. I then tried to do the same via MailMergeDataField, but again in vain, because the document has no data fields, i.e.
ActiveDocument.MailMerge.DataSource.DataFields.Count = 0
At last, I endeavored to follow the example from the DataFields documentation only to find out that the data source has no records:
ActiveDocument.MailMerge.DataSource.RecordCount = -1
Now I give up and ask your help in reading the value of a merge field. Here is a sample document from which I am trying to read the value of the Vendor_ID merge field—400775. Beware that it already contains some VBA code with my failed attempts. The bookmark V_Vendor_Number comprises that value, but I have an explicit requirement not to use bookmarks.
If you have a document that doesn't present you with a mailmerge SQL prompt when you open it, it isn't a mailmerge main document and isn't connected to a data source. Accordingly, all you're left with is reading through the fields collection to find the one you're interested in. You can also take the same approach with a mailmerge main document that is connected to a data source. For example:
Sub Demo()
Dim Fld As Field
For Each Fld In ActiveDocument.Fields
With Fld
If .Type = wdFieldMergeField Then
If Trim(Split(Split(.Code.Text, "MERGEFIELD ")(1),"\")(0)) = "Vendor_ID" Then
MsgBox .Result.Text: Exit For
End If
End If
End With
Next
End Sub

Access recordset (from 2 columns) to Word template (or empty document)

I'm trying to export (button click) data from Access recordset (data from 2 columns - "Question" & "Answer") to Word template which has FormFields "Question" & "Answer".
I managed to do it but only until Word document runs out of FormFields (which I added - 100), if recordset has more than 100 rows, it will result with an error "error 5941 the requested member of the collection does not exist" - because it run out of available FormFields (at this time max number of questions is 1100, and it will grow). I can, btw, add several thousand FormFields, but then user will have to delete the empty ones when he uses this option.
I feel like there is an easy way to implement some other kind of loop which will take a document which has only these two FormFields and copy or duplicate first FormField - "Question" and insert data in second, but I still didn't find any way of doing that.
Exporting data from 2 columns in Access recordset, one is "Question" and other "Answer". Code on ButtonClick:
Private Sub cmdToWordMultiple_Click()
Dim db As DAO.Database
Dim rst As DAO.Recordset
Set db = CurrentDb()
Set rst = Me.RecordsetClone
Set Wd = New Word.Application
Set myDoc = Wd.Documents.Add("C:\Users\User\Desktop\ReportQuestions.docx")
Wd.Visible = True
rst.MoveFirst
Do Until rst.EOF
myDoc.FormFields("Question").Range.Text = Nz(rst!Question, "")
myDoc.FormFields("Answer").Range.Text = Nz(rst!Answer, "")
rst.MoveNext
Loop
rst.Close
Set rst = Nothing
objWord.Application.Quit
End Sub
I would like to make this button export data from Access recordset to Word Document, without leaving excess FormFields.
Most efficient is to save the form fields as Building Blocks which can be inserted as often as required. Building Blocks can only be saved in a template, so open ReportQuestions.docx then use File/Save As to save it as a dotx.
Select the pair of form fields, then use Insert->Quick Parts->Save selection to quick part gallery. Fill out the fields in the dialog box being sure to select the template ReportQuestions.dotx for "Save in".
To insert the form fields, the code would look something like below. Note, also, how to get to the end of the document on each iteration, before inserting the next pair of form fields. (See also note below the code!)
Dim tmpl as Word.Template
Dim sTmplPath as String
sTmplPath = "C:\Users\User\Desktop\ReportQuestions.dotx"
Set myDoc = Wd.Documents.Add(sTmplPath)
Set tmpl = myDoc.AttachedTemplate
Dim rngEndOfDoc as Word.Range
Set rngEndOfDoc = myDoc.Content
rngEndOfDoc.Collapse wdWollapseEnd
tmpl.BuildingBlockEntries("Name of Entry").Insert Where:=rngEndOfDoc, RichText:=True
This builds on the code shown in the question. Based on my experience with form fields, I suspect this code, as shown, does not work for more than one pair of form fields because form field names are also Word bookmarks. And bookmark names must be unique (can only appear once) in a document. So Question and Answer can only be used once. I don't know what you're actually naming the form fields, but you'll probably need to add code to rename the form fields once they've been inserted.
Added from OP's comment: The suggestion was successfully implemented like this
Do Until rst.EOF
tmpl.BuildingBlockEntries("Answer").Insert Where:=rngEndOfDoc, RichText:=True myDoc.FormFields("Answer").Range.Text = "ANSWER: " & Nz(rst!Answer, "")
rst.MoveNext
Loop

How to search database records from access using Microsoft Word using VBA

I'm looking to fill Text Form fields in Word with table entries from the company's Access Database.
So far I have a user form that populates the combo boxes with the company's Project Numbers. I would like to have that when the user submits the client information from the selected project is put into text form fields in the Word document.
My problem is searching the table for the Project Number and accessing the record. When I check the Recordset value after the 'Find First' function it returns the first record in the table.
Here is my Code so far:
'Access Database
Dim db As Database
Dim rst As Recordset
Dim strPath As String
Dim doc As Document
Set doc = ThisDocument
strPath = "string path name"
Set db = OpenDatabase(strPath)
Set rst = db.OpenRecordset("Word Report Query")
rst.FindFirst "Project Number = " & ProjectBox.Value
Using an Access form to select the correct record - should be set up using a combo box and following the wizard.
Word Merge is the feature set of Office where working with Word - one can set up a template that links to an Access table or query or excel sheet - - and that data source is used to insert the data into the Word template/doc.
Alternatively Access can be used to create a Report, and then export that as an rtf Word file.
I ended up using a while loop to search through the database. Probably not the best solution but the only way I could reliably find the recordset.
'Access Database
Dim db As Database
Dim rst As Recordset
Dim strPath As String
Dim doc As Document
Set doc = ThisDocument
strPath = "path name"
Set db = OpenDatabase(strPath)
Set rst = db.OpenRecordset("Word Report Query")
'Find Selected Record
Do While rst![Project Number] <> ProjectBox.Value
rst.MoveNext
Loop
ActiveDocument.FormFields("Company").Result = rst![Client]
ActiveDocument.FormFields("Email").Result = EmployeeBox.Value
ActiveDocument.FormFields("Date").Result = DateBox.Value
If rst![Consultant] <> Null Then
ActiveDocument.FormFields("Addressing").Result = rst![Consultant]
End If
There are no error checks for the project number because I use the database to populate the dropdown box options when the userform is initialized
'Populate Project Box Dropdown
Do While Not rst1.EOF
ProjectBox.AddItem rst1(0)
rst1.MoveNext
Loop

How do I go to my last entered record in a large access form?

I have a large Access database of items (70,000+) with a one-to-many relationship. Each record has a set of analytical results e.g.
record 1 has sample IDs sample1: chlorine=1mg/l, sample 2: chlorine=2.3mg/L;
record 2 has sample IDs sample1: chlorine=3.8mg/L.
I have created a form to begin entering data, but I would like to create a macro button that will take me to the last record entered. Once I start getting to record #100+, I do not want to have to remember the number of the last record I was on.
To complicate this issue, some records do not have analytical data and will be null.
I was thinking of a macro that will search for null records. Once it finds a null record, it will double check that it isn't a previously entered record that happened to be null by checking the next 50 or so records and make sure each 50 records ahead of it are also null. Could someone give me some guidance on how to do this with a macro? Once I have a working macro, I know how to apply it to a button on my form.
Thank you.
You could create a table to store a bookmarked record number from your form:
Then set your form up so you have a command button to bookmark your place:
Then on that button's click event you could take the record number of the current record and put it in your bookmark table for later:
Private Sub cmdSavePlace_Click()
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim sql As String
Set db = CurrentDb
sql = "DELETE * FROM tblLastAccessedRecord"
db.Execute sql
Set rs = db.OpenRecordset("tblLastAccessedRecord")
With rs
rs.AddNew
rs!RecordNumber = Me.CurrentRecord
rs.Update
End With
rs.Close
Set rs = Nothing
Set db = Nothing
End Sub
You can then add a command button to your form to move you to the next record after the saved bookmark using the record number that's currently stored in the table:
The following VBA should jump you to the record after (+1) the one that's been stored in your bookmark table:
Private Sub cmdGoToNext_Click()
Dim db As DAO.Database
Dim rs As DAO.Recordset
Set db = CurrentDb
Set rs = db.OpenRecordset("tblLastAccessedRecord")
DoCmd.GoToRecord acDataForm, "frmDataEntry", acGoTo, rs!RecordNumber + 1
rs.Close
Set rs = Nothing
Set db = Nothing
End Sub
So, for example, if I bookmark record 11...
...this is now stored in our table:
So if we close the form for the day, and come back to form tomorrow we might be back at record 1...
...but if we click our go to bookmarked record command button we'll be taken to record 11+1, i.e. record 12:
Wow, out of the box answer and what I was looking for. The question required a button and one was provided.
Just to build on this a little though, for my purposes, there would be little overhead to a macro / code being fired from the, ummm, afterUpdate control I guess, on the form being edited, that updates the value of the record in tblLastAccessedRecord. Unless OnUnload still has access to the record value, in which case you can set it automatically when you close the form, but I'm not sure it will have.

Accessing another word document TextBox

In my word "sheet", I have a CommandButton of which, when clicked, triggers a certain part of code which basically is about opening a second word document and inserting some informations from the current one (Document1) into the second one (Document2) TextBox.
I have String variables containing text in one word document (e.i. Document1). I am opening a second document (e.i. Document2). Then, I need to reach a specific TextBox from the Document2 and insert into it the value of one of the String variables I already have.
That being said, I can't access that second document (Document2) since I always gets the "4160 error" which result of the file name being incorrect.
Therefore, how can I access my second document (Document2) TextBox and insert into it a specific value I already have?
My code as follow (simplified to one variable since it'll be the same for every other):
Private Sub btn1_Click()
Dim strFile As String
Dim WordApp As New Word.Application
Dim WordDoc As Word.Document
Dim name As String
strFile = "C:\Users\WhateverUser\Desktop\WhateverFolder\Document2.docx"
name= txtBoxName.Text
'This comes from the first document (Document1) which is correct.
' Opening another Word document (Document2)
Set WordDoc = WordApp.Documents.Open(strFile)
WordApp.Visible = True
'Here is the problem
'Trying to access the Document2 TextBox (txtBoxNameDoc2) with various ways but I always get the Incorrect File Name Error
'First I tried
Documents("Document2.docx").Bookmarks("txtBoxNameDoc2").Range.Text = name
'Then I tried
Documents("Document2.docx").txtBoxNameDoc2.Text = name
'And after those, I went looking on internet and tried what I could find but none did work.
End Sub
I can speculate at some errors in the coding you have provided above, but if this line works without returning an error:
Set WordDoc = WordApp.Documents.Open(strFile)
WordApp.Visible = True
THen you should be able to do:
WordDoc.Bookmarks(txtBoxNameDoc2).Range.Text = name
This is because you have already opened "Document2.docx" and furthermore you have specifically assigned it to the WordDoc object variable. Because you have done this, you do not need to explicitly reference it from the Documents collection, as you are doing in your original code.
NB: This assumes that txtBoxNameDoc2 is valid string that identifies a bookmark in the WordDoc document. If it should be interpreted as a literal string (i.e., it is the actual name of the bookmark, then you need to qualify it with quotation marks, like:
WordDoc.Bookmarks("txtBoxNameDoc2").Range.Text = name
If this continues to raise an error, then the named bookmark doesn't exist.
It is possible to assign a bookmark to a TextBox object. Bookmarks do not "automatically" exist in a document, so first you have to ensure such a bookmark exists. You can review these and assign them (if they do not exist) through the ribbon).
Bookmarks don't exist unless you create them. You've assumed that the object's name can also refer to a Bookmark, and while it can, first you need to create the bookmark and assign it the name by which you want to refer it.