nrtd
asked on
Nested Loops
Hi
I want to use a command button on my main form (ClientFrm) then loop through the subform (ContractFrm) to draw out the related records and then loop through the records in the sub form within the sub form (PaymentSubForm) for each Contract.
Main form: ClientFrm
Sub form (single form): ContractFrm
Sub form within ContractFrm (datasheet): PaymentSubForm
The word template would be set up the following way:
[Client Name]
[Contract Ref]
Payment Table Column 1 2 3
Next [Contract Ref]
Payment Table Column 1 2 3
Next [Contract Ref]
Payment Table Column 1 2 3
I have written the following code based on the help I received from GraemeSkan on this post https://www.experts-exchange.com/questions/28624031/How-can-I-export-Form-and-subform-datasheet-to-word-template.html.
However, I have made a mistake somewhere and the loop is just repeating the first Contract Record over and over again.
I would be so grateful if someone could point out where I have gone wrong.
Thanks
N
I want to use a command button on my main form (ClientFrm) then loop through the subform (ContractFrm) to draw out the related records and then loop through the records in the sub form within the sub form (PaymentSubForm) for each Contract.
Main form: ClientFrm
Sub form (single form): ContractFrm
Sub form within ContractFrm (datasheet): PaymentSubForm
The word template would be set up the following way:
[Client Name]
[Contract Ref]
Payment Table Column 1 2 3
Next [Contract Ref]
Payment Table Column 1 2 3
Next [Contract Ref]
Payment Table Column 1 2 3
I have written the following code based on the help I received from GraemeSkan on this post https://www.experts-exchange.com/questions/28624031/How-can-I-export-Form-and-subform-datasheet-to-word-template.html.
However, I have made a mistake somewhere and the loop is just repeating the first Contract Record over and over again.
I would be so grateful if someone could point out where I have gone wrong.
Thanks
N
Private Sub Command36_Click()
On Error GoTo ErrorHandler
'Print customer Contract for current customer.
Dim appWord As Word.Application
Dim docs As Word.Documents
Dim doc As Word.Document 'new
Dim strLetter As String
Dim prps As Object
Dim strTemplateDir As String
Dim tbl As Word.Table
Dim daoRS1 As DAO.Recordset
Dim daoRS2 As DAO.Recordset
Dim myrange As Range
Set appWord = GetObject(, "Word.Application")
strTemplateDir = appWord.Options.DefaultFilePath(wdUserTemplatesPath)
strTemplateDir = strTemplateDir & "\"
Debug.Print "Office templates directory: " & strTemplateDir
strLetter = strTemplateDir & "TableTest.dotx"
Debug.Print "TableTest: " & strLetter
Set docs = appWord.Documents
Set doc = docs.Add(strLetter) 'new
Set prps = appWord.ActiveDocument.CustomDocumentProperties
With prps
.Item("Client Name") = Nz(Me![Client Name])
End With
With appWord
.Visible = True
.Activate
.Selection.WholeStory
.Selection.Fields.Update
.Selection.MoveDown Unit:=wdLine, Count:=1
End With
With doc ' was docs
Set daoRS1 = Forms!ClientFrm!ContractFrm.Form.RecordsetClone ' needs the names of the main and the sub form in here
Set daoRS2 = Forms!ClientFrm!ContractFrm!PaymentSubform.Form.RecordsetClone ' needs the names of the main and the sub form in here
Do While Not daoRS1.EOF
daoRS1.MoveFirst
Set myrange = .Bookmarks("\EndofDoc").Range
With myrange
.InsertAfter Nz(Forms!ClientFrm.ContractFrm.Form.[ContractRef])
End With
Set tbl = .Tables.Add(.Bookmarks("\EndofDoc").Range, 1, 3)
With tbl.Rows.First
.Cells(1).Range.Text = "Payment Number"
.Cells(2).Range.Text = "Due Date"
.Cells(3).Range.Text = "Amount Due"
End With
daoRS2.MoveFirst
Do Until daoRS2.EOF
Set rw = tbl.Rows.Add
With rw
.Cells(1).Range.Text = daoRS2.Fields("PaymentNumber")
.Cells(2).Range.Text = daoRS2.Fields("DueDateG")
.Cells(3).Range.Text = daoRS2.Fields("AmountDue")
End With
daoRS2.MoveNext
Loop
daoRS1.MoveNext
Loop
daoRS2.Close
daoRS1.Close
End With
With appWord
.Visible = True
.Activate
.Selection.WholeStory
.Selection.Fields.Update
.Selection.MoveDown Unit:=wdLine, Count:=1
End With
Set doc = Nothing
Set appWord = Nothing
Exit Sub
ErrorHandlerExit:
Exit Sub
ErrorHandler:
If Err = 429 Then
'Word is not running; open Word with CreateObject
Set appWord = CreateObject("Word.Application")
Resume Next
Else
MsgBox "Error No: " & Err.Number & "; Description: " & Err.Description
Resume ErrorHandlerExit
End If
End Sub
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Jeff - The field names shown are for illustration purposes only and are substitutes for the real ones which I wouldn't necessarily want in the public domain. I am creating an insurance document and this section of code only forms part of it. The whole document that I need to create cannot be achieved through a report sadly.
Gustav - Thank you for your correction, that has stopped the infite loop.
However, I am still not getting the result I need. It is showing the correct ContractRefs for each client but the table of payments that show below the ContractRef are the ones that belong to the first Contract record only. It doesn't seem to want to move past the records of payments past the first contract.
Gustav - Thank you for your correction, that has stopped the infite loop.
However, I am still not getting the result I need. It is showing the correct ContractRefs for each client but the table of payments that show below the ContractRef are the ones that belong to the first Contract record only. It doesn't seem to want to move past the records of payments past the first contract.
Private Sub Command36_Click()
On Error GoTo ErrorHandler
'Print customer Contract for current customer.
Dim appWord As Word.Application
Dim docs As Word.Documents
Dim doc As Word.Document 'new
Dim strLetter As String
Dim prps As Object
Dim strTemplateDir As String
Dim tbl As Word.Table
Dim daoRS1 As DAO.Recordset
Dim daoRS2 As DAO.Recordset
Dim myrange As Range
Set appWord = GetObject(, "Word.Application")
strTemplateDir = appWord.Options.DefaultFilePath(wdUserTemplatesPath)
strTemplateDir = strTemplateDir & "\"
Debug.Print "Office templates directory: " & strTemplateDir
strLetter = strTemplateDir & "TableTest.dotx"
Debug.Print "TableTest: " & strLetter
Set docs = appWord.Documents
Set doc = docs.Add(strLetter) 'new
Set prps = appWord.ActiveDocument.CustomDocumentProperties
With prps
.Item("Client Name") = Nz(Me![Client Name])
End With
With appWord
.Visible = True
.Activate
.Selection.WholeStory
.Selection.Fields.Update
.Selection.MoveDown Unit:=wdLine, Count:=1
End With
With doc ' was docs
Set daoRS1 = Forms!ClientFrm!ContractFrm.Form.RecordsetClone ' needs the names of the main and the sub form in here
daoRS1.MoveFirst
Do While Not daoRS1.EOF
Set myrange = .Bookmarks("\EndofDoc").Range
With myrange
.InsertAfter daoRS1.Fields("[ContractRef]")
End With
Set daoRS2 = Forms!ClientFrm!ContractFrm!PaymentSubform.Form.RecordsetClone ' needs the names of the main and the sub form in here
Set tbl = .Tables.Add(.Bookmarks("\EndofDoc").Range, 1, 3)
With tbl.Rows.First
.Cells(1).Range.Text = "Payment Number"
.Cells(2).Range.Text = "Due Date"
.Cells(3).Range.Text = "Amount Due"
End With
daoRS2.MoveFirst
Do Until daoRS2.EOF
Set rw = tbl.Rows.Add
With rw
.Cells(1).Range.Text = daoRS2.Fields("PaymentNumber")
.Cells(2).Range.Text = daoRS2.Fields("DueDateG")
.Cells(3).Range.Text = daoRS2.Fields("AmountDue")
End With
daoRS2.MoveNext
Loop
daoRS1.MoveNext
Loop
daoRS2.Close
daoRS1.Close
End With
With appWord
.Visible = True
.Activate
.Selection.WholeStory
.Selection.Fields.Update
.Selection.MoveDown Unit:=wdLine, Count:=1
End With
Set doc = Nothing
Set appWord = Nothing
Exit Sub
ErrorHandlerExit:
Exit Sub
ErrorHandler:
If Err = 429 Then
'Word is not running; open Word with CreateObject
Set appWord = CreateObject("Word.Application")
Resume Next
Else
MsgBox "Error No: " & Err.Number & "; Description: " & Err.Description
Resume ErrorHandlerExit
End If
End Sub
That's probably because you here:
Set daoRS2 = Forms!ClientFrm!ContractFr m!PaymentS ubform.For m.Recordse tClone
use the same filtered recordset for whatever is displayed on the contract form.
You will to use the (unfiltered) record source of the payment subform, then - for each loop of contract - filter it to hold the payments for this contract.
/gustav
Set daoRS2 = Forms!ClientFrm!ContractFr
use the same filtered recordset for whatever is displayed on the contract form.
You will to use the (unfiltered) record source of the payment subform, then - for each loop of contract - filter it to hold the payments for this contract.
/gustav
ASKER
Thanks for the speedy reply.
If I have understood correctly I should set daoRS2 to match daoRS1 as follows:
Set daoRS2 = Forms!ClientFrm!ContractFr m.Form.Rec ordsetClon e
However, I then get an error message stating:
Error 3265: Description: Item not found in this collection.
Apologies if I have misunderstood what you were saying.
If I have understood correctly I should set daoRS2 to match daoRS1 as follows:
Set daoRS2 = Forms!ClientFrm!ContractFr
However, I then get an error message stating:
Error 3265: Description: Item not found in this collection.
Apologies if I have misunderstood what you were saying.
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
@nrdt,
I have an application that creates thousands of insurance documents - policies, binders, etc for an infinite number of types . Your current method will work but it is not flexible and requires specific coding for each document type. If you have any interest, I will give you a framework of how it works. For obvious reasons, I can't post it.
I have an application that creates thousands of insurance documents - policies, binders, etc for an infinite number of types . Your current method will work but it is not flexible and requires specific coding for each document type. If you have any interest, I will give you a framework of how it works. For obvious reasons, I can't post it.
ASKER
Gustav - I tried your suggestion and set the DaoRS2 as:
Set daoRS2 = Database2.OpenRecordset("S ELECT [PaymentTbl].[PaymentNumbe r], [PaymentTbl].[DueDateG], [PaymentTbl].[AmountDue], [PaymentTbl].[ContractID] FROM PaymentQry WHERE [PaymentTbl].[ContractID] = Forms!ClientFrm!ContractFr m![Contrac tID]")
However, that then gives me an error message as follows:
Error 3061: Description: Too few parameters. Expected 1.
Set daoRS2 = Database2.OpenRecordset("S
However, that then gives me an error message as follows:
Error 3061: Description: Too few parameters. Expected 1.
Try with:
Set daoRS2 = Database2.OpenRecordset("S ELECT [PaymentTbl].[PaymentNumbe r], [PaymentTbl].[DueDateG], [PaymentTbl].[AmountDue], [PaymentTbl].[ContractID] FROM PaymentQry WHERE [PaymentTbl].[ContractID] = " & Forms!ClientFrm!ContractFr m![Contrac tID] & "")
/gustav
Set daoRS2 = Database2.OpenRecordset("S
/gustav
ASKER
Thanks, that sorted the parameters error message however, it still only returns the payment records for the first contract only.
N
N
Yes, that will be so when you filter on one ContractID. Try removing that:
Set daoRS2 = Database2.OpenRecordset("S ELECT [PaymentTbl].[PaymentNumbe r], [PaymentTbl].[DueDateG], [PaymentTbl].[AmountDue], [PaymentTbl].[ContractID] FROM PaymentQry")
/gustav
Set daoRS2 = Database2.OpenRecordset("S
/gustav
ASKER
If I remove the filter it then gives me all the records in the query.
N
N
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Great. Have a nice weekend!
/gustav
/gustav
ASKER
Using dbOpenDynaset was the last piece of the puzzle to make the code work properly
FWIW, ...Is this basically an "invoice"?
If this data is already in Access, you can probably create a report that looks the same, without all the overhead of looping through form recordsets and modifying Word templates
In Access you would create a simple interface to select a Client, ...then print the invoice
(or simply print out all invoices for all clients...)
it could look something like this:
JeffCoachman