I´m struggling trying to figure out how to insert / attach a file into a table field when clicking on a button in a form. I have been searching on the Internet and have tried many codes but until now, I have not got success.
I found this code below here on stackoverflow but this doesn´t work because apparently I need to define the variables db, rsfile, rsReport and filePath.
The table name is "GC_Eventos" and the field to store the file is "Contrato" where the Event_ID of the form is equal to the Event_ID register in the table
I´m defining db as DAO.Database but i don´t know if the others variables need to be defined as objects or variables or something else. Does someone know how to fix this code or a better way to insert a file into a table on Access using a button??
I would really appreciate your help
Private Sub Command879_Click()
Dim db As DAO.Database
rsfile = db.OpenRecordset("GC_Eventos")
Do While Not rsfile.EOF
If rsfile.Fields("Evento_ID").Value = 1 Then
'Activate edit mode.
rsfile.Edit
'Instantiate the child recordset.
Set rsReport = rsfile.Fields("Contrato").Value
'Add a new attachment.
filePath = "C:\dbPDF\sitereport.pdf"
rsReport.AddNew
rsReport.Fields("FileData").LoadFromFile (filePath)
rsReport.Update
'Update the parent record
rsfile.Update
End If
'Next row
rsfile.MoveNext
Loop
End Sub
Only have to declare variables if module header has Option Explicit line which forces variable declaration. I recommend this be done by default when module is created. From the VBA editor > Tools > Options > Editor > check Require Variable Declaration. Will have to manually add to existing modules.
Dim statements for the 3 variables lacking them:
Dim rsfile As DAO.Recordset
Dim rsReport As DAO.Recordset
Dim filepath As String
Regardless of variable declaration, object variables do need to be Set. You already have one example in your code - rsReport. Add Set to the rsfile = line and add a Set line for db variable.
Set db = CurrentDb
Set rsfile = db.OpenRecordset("GC_Eventos")
Related
I'm attempting to setup a simple function to add the computer name of the person who adds a record into an access database. For example if user on computer 12345 creates a new record into table tblTasks then in the field "Owner" it would put that persons computer name.
The way I'm going about this currently (And not sure it's the best way) is, on the form under the field "Owner" I have set the Default value to =owner and I have created the following function:
Function Owner()
Dim cdb As DAO.Database, rst As DAO.Recordset
Dim wshNet As Object
Set wshNet = CreateObject("Wscript.Network")
Set cdb = CurrentDb
Set rst = cdb.OpenRecordset("SELECT * FROM tblTasks", dbOpenDynaset)
rst.AddNew
rst!Owner = wshNet.Computername
rst.Update '<---- Error starts here
Set wshNet = Nothing
End Function
I'm getting the following error when creating a new record:
I know part of the issue is to do with a validation rule I created not allowing a record to be created without a Task Name but I want to keep that in place.
I'm thinking the issue of the "Owner" field giving an #Error is also to do with the fact that instead of using a button action to create a record I am keeping the ability to just add from the bottom of the record. Which I'd also like to keep in tact.
Any help or advice would be greatly appreciated!
Thanks!!
-Deke
I worked on something similar a few days ago and started using the fields like you. I found a solution similar to the macro that you have.
Create a macro event for the form's BeforeInsert property. Use the below code for the macro and update to fit your needs.
Private Sub Form_BeforeInsert(Cancel As Integer)
Dim Owner As String
Owner = Environ("COMPUTERNAME")
Me!AddedBy= Owner
End Sub
EDIT
Regarding the error that you're receiving, I'm not sure if this will help, but try defining Owner as
Dim wshNet As String
Set wshNet = Environ("COMPUTERNAME")
I was trying something more fancy and did post on accessforums, where I got got no responses and on programmers access, where I got links to more reading material, but which did not help me - probably due to my lack of VBA expertise.
I have done lots of other programming like PHP and Arduino, but VBA is new for me, although I been watching hours of videos, they don't quite cover what I want to do.
After 4 days of researching an failed attempts, I have simplified my approach and I would appreciate some "real" help with actual code.
Scenario:
I have multiple Excel source file with 9 tabs each.
All the source files are in the same directory, (not in the same directory as the database)
Only one source is ever linked.
Each tab of the source file is a linked table within Access.
Objective:
I wish regularly switch source files.
Method:
I want to replace only the connect file property (i.e. the full file path) for each of the 9 sheets/tabs that use the particular file.
The full path must be "picked up" from my form and applied on an event e.g. on closing of form.
Progress:
I have built a form in which I can enter the file name to use and which calculates the full path to the file in question.
I have a button on the form, which is used to close the form.
Code:
Private Sub Form_Close()
Dim dbs As Database
Dim tdf As TableDef
Dim sfl As String
Dim basePath As String
Dim sName As String
Set dbs = CurrentDb
Set sfl = "SourceData_"
Set sName = "JoeSmith"
Set basePath = "D:\Databases\BOM Consolidator\data_source"
' Loop through all tables in the database.
For Each tdf In dbs.TableDefs
If InStr(10, tdf.Connect, sfl, 1) > 10 Then
tdf.Connect = ";DATABASE=" & basePath & sfl & sName & "\" & dbs
Err = 0
On Error Resume Next
tdf.RefreshLink ' Relink the table.
If Err <> 0 Then
End If
End If
Next tdf End Sub
In the above I am entering the path etc directly just to get it working first.
Access froze :(
Help would be appreciated.
Posting this before I try a restart.
After a restart it is not freezing.
It is saying I have a missing object.
The first line is highlighted in yellow, so I assume something must go in the parenthesis, but no idea what.
If it was a function, I would normally put a variable that is not declared inside the function. This being a subroutine, I was not expecting it to ask for something...
Ultimately I will turn it into a function, sothat I can provide the file name.
A clue to what is needed on the first line please...?
Oh also I am using "Code Builder" - is that the correct option to use with closing a form?
I have linked to external data in Visio 2013 and have four different tabs/tables. When a user double clicks a shape the text of that shape will be used to search for a record inthe tables. Note that this data is not linked in the Visio linked data method. Just need to search the table manually not rely on some link between the data record and the shape that is pre-existing.
External data is imported fine. I can capture the text of the double clicked shape. If you can tell me how to run a query against the external data that has been pulled into Visio then HIGHLIGHT the row.
Thank You
Once you have the information regarding the data source (such as server/database name, credentials, etc.), you just need a few lines of VBA in a module like so:
Dim theVariableForDataFieldYouWant as Integer
Dim db as database
Dim rs as recordset
Set db = YourDataConnectionStringHere
Dim sql as string
sql = "Select aFieldYouWant, anotherFieldYouWant from yourTableName WHERE someField = yourCriteria"
set rs = db.openRecordSet(sql)
if rs.eof then
' you recordset is empty, nothing matched
else
theVariableForDataFieldYouWant = rs.fields("theDataField")
end if
rs.close
db.close
set rs = nothing
set db = nothing
If you provide a bit more detail, I can be more precise, but basically that sums it up. Put the code into a function or sub, then call it on your selected event.
I'm deploying an early bound styled VBA module that needs Scripting.Dictionary and RegExp.
The script, predictably, fails when it runs on another computer.
The user has to go to Tools->Reference in the VBA IDE and add a reference to those two libraries manually to make it work.
Hence lies the problem. Asking the non-technical end user to go to the IDE and manually add references is asking way too much of them.
The other alternative is to rewrite the whole (very long script written by someone else) to use late binding. I rather not take this path if there are other methods.
As an altervative, some people suggest adding a reference programatically like so:
Application.VBE.ActiveVBProject.References.AddFromFile [Path to library]
Is this the correct solution and if so are there any downsides of this strategy?
If not, are there other methods that will to enable the code to remain early bound yet does not require references to be added manually by the user.
Suggestions involving direct calls to the Win32/64 API are also welcome.
Thanks.
In my own limited environment (small # of other people using spreadsheets I develop, relatively standard machine setups), if I create the file and add the references, and then give a copy to someone else, they can open it with no problems and not have to do anything, so keep that in mind with this answer. (I'm wondering why that doesn't work for you.) Also, this was with Excel.
Rather than adding a reference from a file path, you might consider using the GUID property instead.
Here is some code I once used to automatically create references in a newly created workbook. (It's part of a script that would export code, references, and unit tests on worksheets to text for use with Subversion and then later reconstitute the workbook from the text files.) You might find it useful to your situation. (EH and cleanup removed to keep it short...)
'Export refs in existing workbook to text file
Private Sub exportRefs_(srcWbk As Workbook)
Dim fs As FileSystemObject
Set fs = New FileSystemObject
Dim tsout As TextStream
Set tsout = fs.CreateTextFile(fs.BuildPath(getTargetPath_(srcWbk), "refs.refs"))
Dim ref As Reference
For Each ref In Application.ThisWorkbook.VBProject.References
Call tsout.WriteLine(ref.GUID)
Next ref
'<EH + cleanup...>
End Sub
'Add refs to newly created workbook based on previously exported text file
Private Sub importRefs_(wbk As Workbook, path As String)
Dim fs As FileSystemObject
Set fs = New FileSystemObject
Dim tsin As TextStream
Set tsin = fs.OpenTextFile(path)
Dim line As String
Dim ref As Reference
While Not tsin.AtEndOfStream
line = tsin.ReadLine()
Set ref = Nothing
On Error Resume Next
Set ref = wbk.VBProject.References.AddFromGuid(line, 0, 0)
On Error GoTo 0
If ref Is Nothing Then
Debug.Print "add failed: " & line
End If
Wend
'<EH + cleanup...>
End Sub
Like, I said, limited environment, but hopefully it helps.
I'm writing a Word/VBA macro for a document template. Every time a user saves/creates a new document from the template, the document needs an ID embedded in the text. How can I (as simple as possible) implement auto-increment for this ID? The ID is numeric.
The system has to have some kind of mechanism to avoid different documents getting the same IDs, but the load is very low. About 20 people will use this template (on our intranet), creating something like 20 new documents a week altogether.
I've toyed with the idea of having a text file that I lock and unlock from the macro, or call a PHP page with an SQLite database, but is there other, smarter solutions?
Note that I can't use UUID or GUID, since the IDs need to be usable by humans as well as machines. Our customers must be able to say over the phone: "... and about this, then, with ID 436 ...?"
Gave some further thought to this, and here is another approach you may want to consider. If you're not interested in a catalog of previous IDs, then you could simply use a custom document property to store the last ID that was used.
In Word 97-2003, you can add a custom property by going to "File / Properties", choosing the custom tab and assigning a name and value there. Adding a custom document property in Word 2007 is a bit more buried and off the top of my head, I think it's "Office Button / Prepare / Document Properties", choose the little drop down box for advanced properties and you'll get the same ol' pre-2007 dialog.
In the example below, I called mine simply "DocumentID" and assigned it an initial value of zero.
The relevant bit of code to update a Custom document property is:
ThisDocument.CustomDocumentProperties("DocumentID").Value = NewValue
As a proof of concept, I created a .dot file and used the following code in the Document_New() event:
Sub UpdateTemplate()
Dim Template As Word.Document
Dim NewDoc As Word.Document
Dim DocumentID As DocumentProperty
Dim LastID As Integer
Dim NewID As Integer
'Get a reference to the newly created document
Set NewDoc = ActiveDocument
'Open the template file
Set Template = Application.Documents.Open("C:\Doc1.dot")
'Get the custom document property
Set DocumentID = Template.CustomDocumentProperties("DocumentID")
'Get the current ID
LastID = DocumentID.Value
'Use any method you need for determining a new value
NewID = LastID + 1
'Update and close the template
Application.DisplayAlerts = wdAlertsNone
DocumentID.Value = NewID
Template.Saved = False
Template.Save
Template.Close
'Remove references to the template
NewDoc.AttachedTemplate = NormalTemplate
'Add your ID to the document somewhere
NewDoc.Range.InsertAfter ("The documentID for this document is " & NewID)
NewDoc.CustomDocumentProperties("DocumentID").Value = NewID
End Sub
Good luck!
You could handle this entirely through VBA using Word and Excel (or Access I suppose, but I have an unnatural aversion towards using Access).
First, create a new Excel workbook and store it in a location that you can access through your word document (mine is C:\Desktop\Book1.xls). You may even want to seed the values by entering a numeric value into cell A1.
In your word document, you would enter this into your Document_Open() subroutine:
Private Sub Document_Open()
Dim xlApp As Excel.Application
Dim xlWorkbook As Excel.Workbook
Dim xlRange As Excel.Range
Dim sFile As String
Dim LastID As Integer
Dim NewID As Integer
'Set to the location of the Excel "database"
sFile = "C:\Desktop\Book1.xls"
'Set all the variables for the necessary XL objects
Set xlApp = New Excel.Application
Set xlWorkbook = xlApp.Workbooks.Open(sFile)
'The used range assumes just one column in the first worksheet
Set xlRange = xlWorkbook.Worksheets(1).UsedRange
'Use a built-in Excel function to get the max ID from the used range
LastID = xlApp.WorksheetFunction.Max(xlRange)
'You may want to come up with some crazy algorithm for
'this, but I opted for the intense + 1
NewID = LastID + 1
'This will prevent the save dialog from prompting the user
xlApp.DisplayAlerts = False
'Add your ID somewhere in the document
ThisDocument.Range.InsertAfter (NewID)
'Add the new value to the Excel "database"
xlRange.Cells(xlRange.Count + 1, 1).Value = NewID
'Save and close
Call xlWorkbook.Save
Call xlWorkbook.Close
'Clean Up
xlApp.DisplayAlerts = True
Call xlApp.Quit
Set xlWorkbook = Nothing
Set xlApp = Nothing
Set xlRange = Nothing
End Sub
I realize this is a tall procedure, so by all means re-factor it to your heart's content. This was just a quick test I whipped up. Also, you'll need to add a reference to the Excel Object Library through References in VBA. Let me know if you have any questions about how that works.
Hope that helps!
Well you have to store the next ID number somewhere. The text file idea is as good as any. You just have to handle the possibility of it being locked or unaccessible for some reason.
Using a database for one number is overkill.
Off the top of my head:
Use Excel as your external DB with Automation.
Explore the several SQLite COM wrappers (Litex comes to mind).
"text file that I lock and unlock from the macro" would be the safest approach.
The DOCID file would only have one number: the last ACTUALLY used ID.
A) You read the file (not in write/append mode) and store on a variable on your document DOC_ID =FILE_ID+1 and save the doc. Tentatively you kill the DOCID file, open/create for read-write sotring your DOC_ID. Close the file. If all went well including Close, you're safe, otherwise, back to A).
You might want to consider: if no file is found create it with this document ID +100, as a measure of recovering from no-UPS disasters whilst in A)
I'm too tired to check if it might create a deadlock under concurrency scenario... it might.
If you feel its worth it, I can put code here.
It seems I found a way to open and update a text file with exclusive rights, which means that there will be no concurrency problems:
Private Function GetNextID(sFile As String) As Integer
Dim nFile As Integer
nFile = FreeFile
On Error Resume Next
Open sFile For Binary Access Read Write Lock Read Write As #nFile
If Err.Number <> 0 Then
' Return -1 if the file couldn't be opened exclusively
GetNextID = -1
Err.Clear
Exit Function
End If
On Error GoTo 0
GetNextID = 1 + Val(Input(LOF(nFile), #nFile))
Put #nFile, 1, CStr(GetNextID)
Close #nFile
End Function
Simply call this function until it doesn't return -1 anymore. Neat.