I'm trying to figure out if there is a way to take a code that I have for merging to a document and set up an if...then that will merge to a different document based on a specific text in a field in a table.
My code that works.
Sub SendConfirmation_Click(CourseNumber As Index)
DoCmd.SetWarnings False
DoCmd.OpenQuery "ConfirmationMailMerge"
Dim LevelIConf As String
Dim OpenWord As Object
'Path to word document
LevelIConf = "G:\POSTPROFESSIONAL\NAIOMT\Classes\PTH536 Level I\LevelIConf.doc"
'Create instance of Word
Set OpenWord = CreateObject("Word.Application")
OpenWord.Visible = True
'Open the document
OpenWord.Documents.Open FileName:=LevelIConf
DoCmd.SetWarnings True
End Sub
I have several Courses that I send out confirmation letters for and each letter is different based on the course. I would like to be able to push a button on the form and have correct document come up based on the course number.
Any help is appreciated. I am a self taught coder and still have lots to learn.
Thanks,
Please note I provide generic solution that may need to be adapted depending on your actual requirements/setup.
Option 1: Your approach (using Word's mail-merge)
You need the following setup:
Two related tables:
Query that returns path to the Conf Letter based on Course ID:
Function that calls the query from previous step:
Function GetConfLetterPathByCourseID(CourseID As Integer) As String
GetConfLetterPathByCourseID = ""
Dim qdf As QueryDef
Dim rs As Recordset
Set qdf = CurrentDb.QueryDefs("GetConfLetterPathByCourseId")
qdf.Parameters("CourseID_par") = CourseID
Set rs = qdf.OpenRecordset
If rs.RecordCount > 0 Then
GetConfLetterPathByCourseID = rs("ConfLetterPath")
End If
End Function
Form with the Send button. Something like this:
And, finally, the Sub for the Send Button:
Sub ConfLetterButton_Click()
DoCmd.SetWarnings False
Dim LevelIConf As String
Dim OpenWord As Object
'Path to word document
LevelIConf = GetConfLetterPathByCourseID(Me.CourseID)
'Create instance of Word
Set OpenWord = CreateObject("Word.Application")
OpenWord.Visible = True
'Open the document
OpenWord.Documents.Open FileName:=LevelIConf
DoCmd.SetWarnings True
End Sub
Please note, I altered slightly your code (e.g. removed Index type, removed Docmd.OpenQuery)
Option 2: Compose email in VBA code and attach the Conf Letter doc file using the Query/Function from option 1. I think this link from Microsoft can provide some details. I did implement similar solution in the past. Worked pretty well.
Related
Good evening!
At this moment I'm learning to work in MS Access for my job purposes. I gained some understanding of the program's basics, such as creating tables or making easy forms (though not yet working ideally), and by now I've got stuck in solving the following task.
I have a database BooksDatabase, which consists of three tables: Books, Authors and AuthorsInfo. First one contains information about books (name, genre, country, release year etc.), third one is about authors (first name, last name etc.) and the second one links ever book with its author(s). The task is to import data from text file to those tables, so that it would be almost automatic. I understand how to import files to MS Access (at least, the ones of *.txt extension) and I do this into the table BooksToImport, but I have some difficulties with inserting imported data. Here is the code of my function ImportBooks(), which I execute from macros of the same name:
' Procedure which imports data about books from the table BooksToImport
Function ImportBooks()
Dim dbBooks As Database
Dim rstImBooks, rstBooks, rstAuthors, rstBALink As DAO.Recordset
Dim codeI, codeB, codeA, codeL As Variant
'initializing database
Set dbBooks = CurrentDb
Set rstImBooks = dbBooks.OpenRecordset("Query_BooksToImport",dbOpenDynaset) 'receiving data from query
'checking if the query has any records
If rstImBooks.RecordCount = 0 Then
MsgBox "There are no records for importing!", vbInformation, "Attention!"
rstImBooks.Close
Set dbBooks = Nothing
Exit Function
End If
'if it's OK, we're making a loop on query's records
rstBooks = dbBooks.OpenRecordset("Books",dbOpenDynaset)
rstAuthors = dbBooks.OpenRecordset("AuthorsInfo",dbOpenDynaset)
rstBALink = dbBoks.OpenRecordset("Authors",dbOpenDynaset)
rstImBooks.MoveLast
rstImBooks.MoveFirst
Do While rstImBooks.EOF = False
'checking if there is a book in out database with the same name as in imported data
codeB = DLookup("[ID]","[Books]","[BookName] = '" & rstImBooks![BookName] & "'")
If IsNull(codeB) Then
'inserting new record
With rstBooks
.AddNew
![BookName] = rstImBooks![BookName]
.Update
.Bookmark = .LastModified
codeB = ![ID]
End With
End If
'in much the same way we're treating the data about authors and making the links
rstImBooks.MoveNext
Loop
rstImBooks.Close
rstBooks.Close
rstAuthors.Close
rstBALink.Close
Set dbBooks = Nothing
End Function
I have two problems with this function:
method .AddNew for rstBooks is not working — MS Access shows me a message with error 438 ("Object doesn't support this property or method");
also I cannot assign variable rstBALink to the recordset because compiler says "Invalid use of property".
So my question is this: how should I solve these two problems? What do I do wrong that my function is not working properly?
A few issues with your code that I see. These may or may not fix your problem.
Your declarations are implicit, meaning you aren't being specific with your code about what your recordset objects are. Instead of using:
Dim rstImBooks, rstBooks, rstAuthors, rstBALink As DAO.Recordset
Try:
Dim rstImBooks As DAO.Recordset
Dim rstBooks As DAO.Recordset
Dim rstAuthors As DAO.Recordset
Dim rstBALink As DAO.Recordset
You can put them all on one line separated by commas, but you still need to declare the type for each or Access will assume it's a variant.
Secondly, recordset objects need to be created using the Set keyword, not by using an = alone.
This was done correctly in the top portion of your code, but is incorrect here:
rstBooks = dbBooks.OpenRecordset("Books",dbOpenDynaset)
rstAuthors = dbBooks.OpenRecordset("AuthorsInfo",dbOpenDynaset)
rstBALink = dbBoks.OpenRecordset("Authors",dbOpenDynaset)
Should be:
Set rstBooks = dbBooks.OpenRecordset("Books",dbOpenDynaset)
Set rstAuthors = dbBooks.OpenRecordset("AuthorsInfo",dbOpenDynaset)
Set rstBALink = dbBooks.OpenRecordset("Authors",dbOpenDynaset)
I think that will solve your issues, but I didn't review every line of your code admittedly. Let me know if you still have problems.
EDIT:
Found a typo:
rstBALink = dbBoks.OpenRecordset("Authors",dbOpenDynaset)
Should be:
Set rstBALink = dbBooks.OpenRecordset("Authors",dbOpenDynaset)
(missed an 'o' in dbBooks)
I camse across an old post which had the pefect solution for my requirement - 'Creating a Document Database Using Microsoft Access' with the answer provided by Renaud BomPuis in the form of a sample database (https://dl.dropboxusercontent.com/u/52900980/StackOverflow/SO25044339.accdb).
I have been able to manipulate the source code for this to suit my needs and successfully insert it into my main database. The only problem I have is that it creates a new record at the wrong point for me. When the user clicks 'Upload File' a new record is created and a form opens to be able to select the file using file dialog. But if the user changes their mind and clicks cancel, the record is already created but empty of a file path.
I would like to be able to only create a new record if the user confirms it but I cannot seem to manipulate the code into the correct order for it to work.
Can anybody help please? Many thanks.
EDIT: Code from comment
Private Sub btnUploadDoc_Click() ' Create a new record in the Documents table for the selected Works No
Dim DocID As Variant
Dim db As dao.Database
Dim rs As dao.Recordset
Set db = CurrentDb()
Set rs = db.OpenRecordset("tblDocuments", dbOpenDynaset, dbFailOnError)
With rs
.AddNew !WorksNo = cboWorksNo
.Update
.Move 0, .LastModified
DocID = !DocID
.Close
End With
Set rs = Nothing
Set db = Nothing
DoCmd.OpenForm "frmDocSelect", WhereCondition:="DocID=" & DocID
End Sub
This will not be a trivial change, since (I assume) frmDocSelect depends on an existing record in tblDocuments.
The best way to proceed is probably to simply delete the new record if the user clicks Cancel.
Something like
Sub cmdCancel_Click()
Dim DocID As Long
DocID = Me.DocID
' Close form before deleting, to avoid a flicker of "#Deleted"
DoCmd.Close acForm, Me.Name, acSaveNo
CurrentDb.Execute "DELETE * FROM tblDocuments WHERE DocID=" & DocID
End Sub
I have a simple_table with 4 fields:
a,b,x,P
I am trying to update the field p based on the output of a function that uses the other fields as input parameters. In this case the function is an excel function.
I was using SQL server but really need to access some statistical functions. So yesterday I opened access for the first time. Eeek. I've spent the last day trying to learn vba and following various tutorials on recordsets.
The bit I'm struggling with is how to I update a the P field based on the other fields? In a loop?
Thanks very much.
Dim objExcel As Excel.Application
Set objExcel = CreateObject("Excel.Application")
'Test it works
MsgBox objExcel.Application.BetaDist(0.4, 2, 5)
'OK, that works :)
'set up the ADO stuff
Dim cnn1 As ADODB.Connection
Dim MyRecordSet As New ADODB.Recordset
Set cnn1 = CurrentProject.Connection
MyRecordSet.ActiveConnection = cnn1
'Load data into MyRecordSet
MySQLcmd = "SELECT * FROM simple_table"
MyRecordSet.Open MySQLcmd
'HELP WITH THE NEXT BIT PLEASE!
'Some kind of loop to go through the recordset to set the field P
' equal to the result of the excel function betadist(x,a,b)
'I imagine looping through something like the following semi pseudo code ???
myRecordSet.Fields(“P”).Value = objExcel.Application.BetaDist(myRecordSet.Fields(“x”).Value, myRecordSet.Fields(“a”).Value, myRecordSet.Fields(“b”).Value)
'end of the loop
objExcel.Quit
Set objExcel = Nothing
MyRecordSet.Close
cnn1.Close
Set MyRecordSet = Nothing
Set cnn1 = Nothing
Since your code works with "Dim objExcel As Excel.Application", that means you have a reference set for the Excel object library. In that case, you don't need a full Excel application instance in order to use the BetaDist function. You can set an object variable to Excel.WorksheetFunction and call the function as a method of that object. However, I don't know whether that makes a significant difference. I didn't test the CreateObject("Excel.Application") alternative.
In this sample, I used a DAO recordset instead of ADO. The reason is I've found DAO can be significantly faster with native Access (Jet/ACE) data sources. You can switch to ADO if you prefer, but I don't see an advantage.
Notice I opened the table directly rather than via a query. The DAO dbOpenTable option can also benefit performance.
With those details out of the way, it's just a simple matter of looping through the recordset, calling the function with values from the current row, and storing the function's result in the P field ... pretty much what you outlined in your pseudo-code. :-)
Dim objWFunction As Object ' Excel.WorksheetFunction
Dim MyRecordSet As DAO.Recordset
Dim db As DAO.database
Set objWFunction = Excel.WorksheetFunction ' Excel reference required
Set db = CurrentDb
Set MyRecordSet = db.OpenRecordset("simple_table", dbOpenTable)
With MyRecordSet
Do While Not .EOF
'Debug.Print objWFunction.BetaDist(!x, !a, !b)
.Edit
!p = objWFunction.BetaDist(!x, !a, !b)
.Update
.MoveNext
Loop
.Close
End With
Set MyRecordSet = Nothing
Set db = Nothing
Set objWFunction = Nothing
I actually have 2 questions:
1. How might I see who is using my Access database?
E.g: There is someone with an Access database opened and it created the .ldb file, I would like to see a list of who opened that database (it could be more than one person).
2. How might I see who is using a linked table?
E.g: I have 10 different Access databases, and all of them are using a same linked table. I would like to see who is using that linked table.
I don't even know if it's really possible, but I really appreciate your help!
For you information: The main problem is that lots of people use the same Access in the same network drive, so when I need to change it I have to kick them all out, but I never know who is actually using it.
Update: Rather than reading and parsing the .ldb/.lacdb file, a better approach would be to use the "User Roster" feature of the Access OLEDB provider as described in the Knowledge Base article
https://support.microsoft.com/en-us/kb/285822
and in the other SO question
Get contents of laccdb file through VBA
Original answer:
I put together the following a while ago. It looked promising but then I discovered that computers are not immediately removed from the lock file when they disconnect. Instead, Jet/ACE seems to (internally) mark them as inactive: If ComputerA disconnects and then ComputerB connects, ComputerB overwrites ComputerA's entry in the lock file.
Still, it does provide a list of sorts. I'm posting it here in case somebody can offer some suggestions for refinement.
I created two tables in my back-end database:
Table: [CurrentConnections]
computerName Text(255), Primary Key
Table: [ConnectionLog]
computerName Text(255), Primary Key
userName Text(255)
A VBA Module in my back-end database contained the following code to read (a copy of) the lock file and update the [CurrentConnections] table:
Public Sub GetCurrentlyConnectedMachines()
Dim cdb As DAO.Database, rst As DAO.Recordset
Dim fso As Object '' FileSystemObject
Dim lck As Object '' ADODB.Stream
Dim lockFileSpec As String, lockFileExt As String, tempFileSpec As String
Dim buffer() As Byte
Set cdb = CurrentDb
cdb.Execute "DELETE FROM CurrentConnections", dbFailOnError
Set rst = cdb.OpenRecordset("SELECT computerName FROM CurrentConnections", dbOpenDynaset)
lockFileSpec = Application.CurrentDb.Name
If Right(lockFileSpec, 6) = ".accdb" Then
lockFileExt = ".laccdb"
Else
lockFileExt = ".ldb"
End If
lockFileSpec = Left(lockFileSpec, InStrRev(lockFileSpec, ".", -1, vbBinaryCompare) - 1) & lockFileExt
'' ADODB.Stream cannot open the lock file in-place, so copy it to %TEMP%
Set fso = CreateObject("Scripting.FileSystemObject") '' New FileSystemObject
tempFileSpec = fso.GetSpecialFolder(2) & "\" & fso.GetTempName
fso.CopyFile lockFileSpec, tempFileSpec, True
Set lck = CreateObject("ADODB.Stream") '' New ADODB.Stream
lck.Type = 1 '' adTypeBinary
lck.Open
lck.LoadFromFile tempFileSpec
Do While Not lck.EOS
buffer = lck.Read(32)
rst.AddNew
rst!computerName = DecodeSZ(buffer)
rst.Update
buffer = lck.Read(32) '' skip accessUserId, (almost) always "Admin"
Loop
lck.Close
Set lck = Nothing
rst.Close
Set rst = Nothing
Set cdb = Nothing
fso.DeleteFile tempFileSpec
Set fso = Nothing
End Sub
Private Function DecodeSZ(buf() As Byte) As String
Dim b As Variant, rt As String
rt = ""
For Each b In buf
If b = 0 Then
Exit For '' null terminates the string
End If
rt = rt & Chr(b)
Next
DecodeSZ = rt
End Function
The following code in the Main_Menu form of the front-end database updated the [ConnectionLog] table
Private Sub Form_Load()
Dim cdb As DAO.Database, rst As DAO.Recordset
Dim wshNet As Object '' WshNetwork
Set wshNet = CreateObject("Wscript.Network")
Set cdb = CurrentDb
Set rst = cdb.OpenRecordset("SELECT * FROM ConnectionLog", dbOpenDynaset)
rst.FindFirst "ComputerName=""" & wshNet.computerName & """"
If rst.NoMatch Then
rst.AddNew
rst!computerName = wshNet.computerName
Else
rst.Edit
End If
rst!userName = wshNet.userName
rst.Update
Set wshNet = Nothing
End Sub
Finally, the following form in the back-end database listed [its best guess at] the current connections
It is a "continuous forms" form whose Record Source is
SELECT CurrentConnections.computerName, ConnectionLog.userName
FROM CurrentConnections LEFT JOIN ConnectionLog
ON CurrentConnections.computerName = ConnectionLog.computerName
ORDER BY ConnectionLog.userName;
and the code-behind is simply
Private Sub Form_Load()
UpdateFormData
End Sub
Private Sub cmdRefresh_Click()
UpdateFormData
End Sub
Private Sub UpdateFormData()
GetCurrentlyConnectedMachines
Me.Requery
End Sub
Easy. Open the .ldb file in notepad (or any text editor) and you can see the machine names.
RE: How might I see who is using my Access database?
•E.g: There is someone with an Access database opened and it created the .ldb file, I would like to see a list of who opened that database (it could be more than one person).
Just happened across this while looking for something else, and I thought I might share what I do for this. Note that this assumes that the host computer (the computer on which the database file resides) uses file sharing to provide access to the file.
You will need to be on the host computer, or have authority to connect to that machine.
click Start
right-click My Computer and select Manage
if you're not on the host computer, right-click 'Computer Management' and enter the host's name
Expand 'Shared Folders' and click on 'Open Files'
At the right is the list of currently open files with the username for each current user
I agree with Gord's Original answer. I used this code on my database, it seems that there is a way around computers not being taken out of CurrentConnections upon exit of the DB.
I placed this on my main menu form because it is always open until the user exits. I used the unload event on my form to get this to work, and it works awesome! Here is my code
p.s. Ignore SetWarnings I just have that on so the user doesn't have to click through prompts.
Private Sub Form_Unload(Cancel As Integer)
Dim wshNet As Object
Dim deleteSQL As String
Set wshNet = CreateObject("WScript.Network")
DoCmd.SetWarnings False
deleteSQL = "DELETE tblCurrentConnections.* " & _
"FROM tblCurrentConnections WHERE[computerName] = '" & wshNet.computerName & "';"
DoCmd.RunSQL deleteSQL
DoCmd.SetWarnings True
End Sub
I need help coming up with a method to allow a user to export a query's results to an xls file on a button click event.
I've tried using an Output To macro, but it doesn't work for a query containing 30,000+ records.
Thanks in advance
You might want to consider using Automation to create an Excel spreadsheet and populate it on your own rather than using a macro.
Here's a function I have used in the past to do just that.
Public Function ExportToExcel(FileToCreate As String, ByRef rst As ADODB.Recordset)
'Parms: FileToCreate - Full path and file name to Excel spreadsheet to create
' rst - Populated ADO recordset to export
On Error GoTo Err_Handler
Dim objExcel As Object
Dim objBook As Object
Dim objSheet As Object
'Create a new excel workbook; use late binding to prevent issues with different versions of Excel being
'installed on dev machine vs user machine
Set objExcel = CreateObject("Excel.Application")
Set objBook = objExcel.Workbooks.Add
'Hide the workbook temporarily from the user
objExcel.Visible = False
objBook.SaveAs (FileToCreate)
'Remove Worksheets so we're left with just one in the Workbook for starters
Do Until objBook.Worksheets.Count = 1
Set objSheet = objBook.Worksheets(objBook.Worksheets.Count - 1)
objSheet.Delete
Loop
Set objSheet = objBook.Worksheets(1)
rst.MoveFirst
'Use CopyFromRecordset method as this is faster than writing data one row at a time
objSheet.Range("A1").CopyFromRecordset rst
'The UsedRange.Rows.Count property can be used to identify the last row of actual data in the spreadsheet
'This is sometimes useful if you need to add a summary row or otherwise manipulate the data
'Dim lngUsedRange As Long
'lngUsedRange = objSheet.UsedRange.Rows.Count
'Save the spreadsheet
objBook.Save
objExcel.Visible = True
ExportToExcel = True
Err_Handler:
Set objSheet = Nothing
Set objBook = Nothing
Set objExcel = Nothing
DoCmd.Hourglass False
If Err.Number <> 0 Then
Err.Raise Err.Number, Err.Source, Err.Description
End If
End Function
Can you use VBA?
Intellisense will help you, but get started with:
DoCmd.TransferSpreadsheet acExport, acSpreadsheetTypeExcel9, "my_query_name", "C:\myfilename.xls"
Note: you may have a different Excel version
"my_query_name" is the name of your query or table
you'll need to set the file location to the appropriate location\name .extension
More Info: http://msdn.microsoft.com/en-us/library/bb214134.aspx