MS Access 2013 saved append query not updating all fields - ms-access

I have a saved query, qryInsertLog which is as follows:
PARAMETERS UserIDPar Long, UnitIDPar Long, LogEntryPar LongText, FNotesPar LongText;
INSERT INTO tblLogBook ( UserID, UnitID, LogEntry, FNotes )
SELECT [UserIDPar] AS Expr1, [UnitIDPar] AS Expr2, [LogEntryPar] AS Expr3, [FNotesPar] AS Expr4;
I'm trying to run this query when a save button is clicked on an unbound form, where the parameters are gathered from the form controls. My VBA code for the save button is:
Private Sub cmdSave_Click()
Dim db As DAO.Database
Dim qdf As DAO.QueryDef
Dim okToSave As Boolean
If Me.cboUser.Value = 0 Or IsNull(Me.cboUser.Value) Then
MsgBox "You must choose a user. Record not saved."
okToSave = False
ElseIf Me.cboUnit.Value = 0 Or IsNull(Me.cboUnit.Value) Then
MsgBox "You must choose a unit. Record not saved."
okToSave = False
ElseIf Me.txtLogEntry.Value = "" Or IsNull(Me.txtLogEntry.Value) Then
MsgBox "You must have somtehing to log. Record not saved."
okToSave = False
Else
okToSave = True
End If
Set db = CurrentDb
Set qdf = db.QueryDefs("qryInsertLog")
qdf.Parameters("UserIDPar").Value = Me.cboUser.Value
qdf.Parameters("UnitIDPar").Value = Me.cboUnit.Value
qdf.Parameters("LogEntryPar").Value = Me.txtLogEntry.Value
qdf.Parameters("FNotesPar").Value = IIf(IsNull(Me.txtFNotes.Value), "", Me.txtFNotes.Value)
If okToSave Then
qdf.Execute
End If
qdf.Close
Set qdf = Nothing
End Sub
When this code is run, the FNotes field of the table isn't updated. The other three fields update as expected. FNotes is the only field which isn't required. I hardcoded a string for FNotes paramater like so:
qdf.Parameters("FNotesPar").Value = "why doesn't this work"
rather than using the form control value, and got the same result: that field just doesn't update. When I run this query from the Access Objects window and supply parameter values from the prompts, it works just fine. When I create form that's bound to the table, it also seems to work just fine.
I can't figure out why there's no trouble updating the LogEntry field but the FNotes field fails to update.

Add the new record via a DAO.Recordset instead of a DAO.QueryDef.
First, include this declaration ...
Dim rs As DAO.Recordset
Then use this after Set db = CurrentDb ....
Set rs = db.OpenRecordset("tblLogBook")
With rs
If okToSave Then
.AddNew
!UserID = Me.cboUser.Value
!UnitID = Me.cboUnit.Value
!LogEntry = Me.txtLogEntry.Value
!FNotes = Nz(Me.txtFNotes.Value, "")
.Update
End If
.Close
End With
Note Nz(Me.txtFNotes.Value, "") gives you the same thing as IIf(IsNull(Me.txtFNotes.Value), "", Me.txtFNotes.Value), but more concisely.

Related

How can I get the value from a control using the name of the control source?

I am trying to write some code to audit changes made via a form. I have a function that works to do this:
Function WriteChanges()
Dim f As Form
Dim c As Control
Dim user As String
Dim db As DAO.Database
Dim cnn As ADODB.Connection
Dim MySet As ADODB.Recordset
Dim tbld As TableDef
Dim recsource As String
Set f = Screen.ActiveForm
Set db = CurrentDb
Set cnn = CurrentProject.Connection
Set MySet = New ADODB.Recordset
recsource = f.RecordSource
Set tbld = db.TableDefs(recsource)
pri_key = fFindPrimaryKeyFields(tbld)
Debug.Print "pri_key: "; pri_key
user = Environ("username")
MySet.Open "Audit", cnn, adOpenDynamic, adLockOptimistic, adCmdTable
For Each c In f.Controls
Select Case c.ControlType
Case acTextBox, acComboBox, acListBox, acOptionGroup
If c.Value <> c.OldValue Then
With MySet
.AddNew
![EditDate] = Now
![user] = user
![SourceTable] = f.RecordSource
![SourceField] = c.ControlSource
![BeforeValue] = c.OldValue
![AfterValue] = c.Value
.update
End With
End If
End Select
Next c
MySet.Close
Set MySet = Nothing
Set f = Nothing
Set db = Nothing
End Function
I use this function on the before update property of various forms and it populates the Audit table with the details of the changes to values in each of the controls. I need to also get the value from the primary key field to add to the Audit table. I can use the following code to identify the name of the primary key within the form's record source:
Function fFindPrimaryKeyFields(tdf As TableDef) As String
Dim idx As Index
On Error GoTo HandleIt
For Each idx In tdf.Indexes
If idx.Primary Then
fFindPrimaryKeyFields = Replace(idx.Fields, "+", "")
GoTo OutHere
End If
Next idx
OutHere:
Set idx = Nothing
Exit Function
HandleIt:
Select Case Err
Case 0
Resume Next
Case Else
Beep
MsgBox Err & " " & Err.Description, vbCritical + vbOKOnly, "Error"
fFindPrimaryKeyFields = vbNullString
Resume OutHere
End Select
End Function
How can I use this to get the value from the control (text box) that has the identified primary key as its control source.
Please forgive any silly errors in my code as I'm a relative novice. Thanks in advance for any help.
I'm not 100% sure what you want exactly, but if you have the name of the field, you can use the following:
Dim primaryKeyValue As Variant
Dim primaryKeyColumnName As String
primaryKeyColumnName = fFindPrimaryKeyFields(TableDefs!MyTable)
Dim f as Form
'Get the form somehow
Dim c As Control
On Error GoTo NextC 'Escape errors because lots of controls don't have a control source
For Each c In f.Controls
If c.ControlSource = primaryKeyColumnName Then
primaryKeyValue = c.Value
End If
NextC:
Next c
On Error GoTo 0
If the primary key column is part of the form record source, you can simply read it by:
Debug.Print "PK value: " & f(pri_key)
Every column of the record source is a form property at runtime, independent of whether there is a control with the same name.
Note: your whole construct will stop working if you have a form that is based on a query that joins multiple tables.

Tracking value changes when editing a record in Access

I am trying to track value changes when editing a record. All fields on the form are unbound text box.
Below is a function that is used to insert a audit tracking record.
Public Function AuditChanges(RecordID As String, UserAction As String)
Dim DB As Database
Dim rst As Recordset
Dim clt As Control
Dim Userlogin As String
Set DB = CurrentDb
Set rst = DB.OpenRecordset("select * from tbl_audittrail", adOpenDynamic)
Userlogin = Environ("username")
Select Case UserAction
Case "Edit"
For Each clt In Screen.ActiveForm.Controls
If (clt.ControlType = acTextBox _
Or clt.ControlType = accombox) Then
If Nz(clt.Value) <> Nz(clt.OldValue) Then
With rst
.AddNew
![DateTime] = Now()
!UserName = Userlogin
!FormName = Screen.ActiveForm.Name
!Action = UserAction
!RecordID = Screen.ActiveForm.Controls(RecordID).Value
!FieldName = clt.ControlSource
!OldValue = clt.OldValue
!Newvalue = clt.Value
.Update
End With
End If
End If
Next clt
End Select
rst.Close
DB.Close
Set rst = Nothing
Set DB = Nothing
End Function
Below is how I use the function:
Private Sub btnUpdate_Click()
Set DB = CurrentDb
Set rs = DB.OpenRecordset("SELECT * FROM ASID", dbOpenDynaset, dbSeeChanges)
rs.Edit
rs!ISIN = Me.ISIN
rs!SECIDTYPE = Me.SECIDTYPE
rs!ALTSECID = Me.ALTSECID
rs.Update
Call AuditChanges("ISIN", "Edit")
End If
End Sub
The problem is when it calls AuditChanges, it goes directly from
If (clt.ControlType = acTextBox _
Or clt.ControlType = accombox)
to End If
All fields on current form are unbound text boxes and you have to press an "Add" command button to actually add a record. I think there must be something wrong with the control type but I am not sure which control type should be used. Any idea?
Start by putting Option Explicit at the top of each module.
It enforces variable declaration and reports undeclared or misspelled variables/constants at compile time.
To have this automatically in new modules, set the Require Variable Declaration option in the VBA Editor.
This is really a must have for VBA development.
Then the compiler will tell you that accombox doesn't exist, it should be acComboBox
Without Option Explicit, accombox is initialized as NULL variant, and causes your entire If condition to be NULL, and therefore never be entered.

Access VB: Validating blank values and incorrect values in a textbox

I have a textbox where the user inputs the ID of a row to be updated/edited. I got it to work properly when they enter an ID that exists, but I get an error when they input an ID that doesn't exist or when they leave it blank.
Goals:
Allow blank - if the user leaves the field blank, then I need to wipe the form and simply let the user continue (no message box needed).
Warn user when ID is not valid with a message box.
Current Code
Private Sub Text135_LostFocus()
If Me.Text135 = Nothing Then
MsgBox "Nothing entered into the ID field. Query will not run"
GoTo Last_Line:
Else
sql = "SELECT * FROM tbl_Downtime WHERE ID = " & Forms![DTForm]![Text135] & ";"
Set db = CurrentDb
Set rs = db.OpenRecordset(sql)
Me.Text126.Value = rs!production_date
Me.Text144.Value = rs!shift
Me.Text116.Value = rs!job
Me.Text118.Value = rs!suffix
Me.Text121.Value = rs!reason
Me.Text123.Value = rs!downtime_minutes
Me.Text4.Value = rs!people_missing
Me.Text128.Value = rs!comment
Set db = Nothing
Set rs = Nothing
Last_Line:
End If
End Sub
Completely stop executing the sub when the field is blank:
If Nz(Me.Text135) = "" Then 'Text135 is null or empty
Exit Sub
End If
But if the code in your question is your actual code (and not just a shortened example), you need neither the Exit Sub nor the GoTo Last_Line: part, because after the message box, the code execution will jump to the End If anyway.
Check whether the Recordset contains any rows:
Set db = CurrentDb
Set rs = db.OpenRecordset(sql)
If rs.EOF Then
'rs.EOF is True when there are no rows
MsgBox "ID is not valid"
Else
'do stuff
End If
Set db = Nothing
Set rs = Nothing

vba access run time error 3265

I am new to programming in VBA. I am trying to copy Form data from an existing form when I click on the Copy Record button. This is supposed to copy the current form data as a new record with a new master_id (that is autonumbered) and have Brand as blank field for them to fill in. I get a:
Run Time Error 3265 "Item not found in this collection"
at the new_master_id that i created. I am not sure how to fix this problem. Any help is appreciated.
Private Sub Copy_Record_Click()
Dim RS As DAO.Recordset, C As Control
Dim FillFields As String, FillAllFields As Integer
Dim New_MASTER_ID As Integer
New_MASTER_ID = (DMax("[MASTER_ID]", "tbl_Drug_Master") + 1)
Dim BRAND As String
BRAND = ""
Set RS = CurrentDb.OpenRecordset(Name:="tbl_Drug_Master", Type:=RecordsetTypeEnum.dbOpenDynaset)
With RS
.AddNew
![MASTER_ID] = ![New_MASTER_ID] <--this is where the problem is...
![MASTER_KEY] = Me![MASTER_KEY]
![PRODUCT_CATEGORY] = Me![PRODUCT_CATEGORY]
![BRAND] = Me![BRAND]
![GENERIC] = Me![GENERIC]
![STUDY_NAME] = Me![STUDY_NAME]
![MANUFACTURER] = Me![MANUFACTURER]
![MASTER_COMMENTS] = Me![MASTER_COMMENTS]
.Update
End With
End Sub
ok so firstly, im not sure why the following are required:
dim c as control
Dim FillFields As String, FillAllFields As Integer
New_MASTER_ID = (DMax("[MASTER_ID]", "tbl_Drug_Master") + 1)
Dim BRAND As String
BRAND = ""
therefore I am leaving them out as part of this question because they appear unnecessary. Brand is not required because you are creating a new record and putting nothing in the brand field so it will remain blank.
I am also not too sure why you have 2 tables both that are the same? I think what should happen is that you simply copy the data to a new record in the same table.
You will see I have put a save record command in to the routine. other additions such as error handling is also recommended.
Private Sub Copy_Record_Click()
docmd.runcommand accmdsaverecord
Dim RS As Recordset
Set RS = CurrentDb.OpenRecordset(Name:="tbl_Drug_Master", Type:=RecordsetTypeEnum.dbOpenDynaset)
With RS
.AddNew
![MASTER_KEY] = Me.MASTER_KEY.value
![PRODUCT_CATEGORY] = Me.PRODUCT_CATEGORY.value
![GENERIC] = Me.GENERIC.value
![STUDY_NAME] = Me.STUDY_NAME.value
![MANUFACTURER] = Me.MANUFACTURER.value
![MASTER_COMMENTS] = Me.MASTER_COMMENTS.value
.Update
End With
Set RS = Nothing
End Sub
I was mistaken with my comment rs.close it would be db.close but you are using the currentdb and no reason to close it. This procedure will remain on the original record, if you want to go to the new record you will have to add a command like docmd.gotorecord acdataform, , aclast before the end of the routine.

how to read one field from one record

I know I'm over thinking this, but I want to check a single value/field within a single record. For instance, I want to know if the value of the "closedDate" field in the record with the primary key of 33 is null or not.
I was thinking something like:
dim db as DAO.Database
dim rs as DAO.Recordset
set db = CurrentDb
set rs = db.OpenRecordset("record_holdData")
If not isNull(rs.Fields("closedDate")) then
'do nothing
Else
'add a close date
End If
But I don't think this is right. It doesn't specify the record number. In the application, the form is opened by being bound to the record in question, but I don't think CurrentDb takes that into consideration and rather references the entire table.
So my question is, how do open the recordset in this fashion and reference this field in that particular record only?
You found the answer you wanted, but I would use the DLookup Function instead.
Dim db As DAO.Database
Dim strWhere As String
Dim varClosedDate As Variant
Set db = CurrentDb
strWhere = "id = 33"
varClosedDate = DLookup("closedDate","record_holdData",strWhere)
If IsNull(varClosedDate) = True Then
'use today's date as closedDate
db.Execute "UPDATE record_holdData Set closedDate = Date() WHERE " & strWhere
End If
I was under the impression that the argument for the .OpenRecordset() method was to be a table name only. But it turns out you can send it a query too:
set rs = db.OpenRecordset("select * from record_holdData where id = 33")