MS Access vba save button error 3021 - vba

For various reasons, I want to set up a custom button on my forms to save the current record. I use a navigation form and want to trigger the same process (integrity-checks, user input etc.) whenever the entry is saved, thus whenever the user presses the "save"-button or switches to another form. The user will conditionally be asked to confirm the process and is thus able to cancel it as well.
Everything is running smoothly with one really odd and annoying exception: Whenever I click the save button on a new record and prompt a message within the "BeforeUpdate" event, I receive
RTE 3021 ("no current record")
Without the MsgBox, everything is fine. Even more strange:
When I trigger the save process by switching to another form using the navigation form (or simply press "outside" the form used for data entry), everything is fine as well.
Here is a minimalistic example (similar results with DoCmd.Save, Requery or acCmdSaveRecord):
Private Sub vt_save_Click()
Me.Dirty = False
End Sub
Private Form_BeforeUpdate(Cancel As Integer)
Cancel = True
MsgBox "Test"
End Sub
Any ideas? I simply can't wrap my head around that error.

You could maybe try to run a query using the values in the form while checking if the record exists or not.
Is there a primary key on the table? if so, the primary key will be your focal point.
Private Sub vt_Save_Click()
dim rst as DAO>Recordset
Dim strSQL as String
Dim strID as string
strID = me.YourPrimaryKeyField
strSQL = "SELECT * " & _
"FROM YourTableName " & _
"WHERE (((YourTableName.YourFieldName) =" & me.PrimaryKeyField & "));"
set rst = currentdb.openrecordset(strsql)
if rst.recordcount = 0 then
currentdb.execute "INSERT INTO YourTableName ( List All Fields to Add ) " & _
"SELECT List All Field controls with values to add;"
End IF
'Anything else you want the code to do from here
EndCode:
If not rst is nothing then
rst.close
set rst = nothing
End IF
End Sub
Repeat this process for the Form_LostFocus() event. If you want to make it easier, make this code a module and call within both event triggers on your form.
If this doesn't work please let me know and I will be happy to further assist.

The most straight forward and reasonable solution is to use an Error Handler - which I ignored so far tenaciously.
Private Sub save_Click()
On Error GoTo Err_Handler
Me.Dirty = False
Exit_Here:
Exit Sub
Err_Handler:
If Err.Number = 2101 Then
'ignore or message
Else
MsgBox Err.Description
End If
Resume Exit_Here
End Sub

Related

prevent duplicates when passing values between two forms (with Args)

I have two forms: transfert Form with its subform and intransfert Form. I am using
DoCmd.OpenForm "intransfert", , , , acFormAdd, acDialog, Me!Text83
(where text83 is =[transfertasubform].[Form]![transfertadetailid] under
Private Sub Command78_Click()
in transfet form and
Private Sub Form_Load()
On Error Resume Next
If Me.NewRecord Then Me!trnrin = Me.OpenArgs
in intransfet form. intransfert form is based in transfertdetailquery. i wont to prevent passing text83 value more then one time
i tried to explain my problem and expect a help to prevent duplicates when used Arge
assuming trnrin is the name of a variable in your record source. assuming you mean that you want to avoid adding two records where trnrin has the same value and the user sent the same open args twice. assuming trnrin is also the name of a control in the detail section of the intransfert form.
'form load only runs when the form is opened so you have to close the form to pass new args
'but you can just move the same code to a button or whatever
Private Sub IntransferForm_Load()
If Len(Me.OpenArgs) > 0 Then
Me.txtBoxintheHeader = Me.OpenArgs 'the load event is the right place to set controls
'most of this code is to check if there is already an instance where trnrin = the OpenArgs in the record source
Dim lookedupArgs As String
lookedupArgs = Nz(lookedupArgs = DLookup("trnrin", "MyTable", "trnrin = " & "'" & Me.OpenArgs & "'"), "ValuethatisneveranArg")
If lookedupArgs = "ValuethatisneveranArg" Then
'Debug.Print "trnrin = '" & Me.OpenArgs & "'" 'note the string delimiters
'Me.trnrin = Me.OpenArgs 'this surprisingly works but will break easily and doesn't handle complex cases
Dim rs As Recordset
Set rs = Me.Recordset 'if recordset is bound to a table then table will also be updated.
'you can also bind to the table directly but you may need to call me.requery to show the changes
rs.AddNew
rs!trnrin = Me.OpenArgs
rs.Update
End If
End If
End Sub

Form event procedure not moving to last record

I am experiencing some odd behavior from a form I am creating in an access database. I have a form called frmSeedling with a subform called frmSeedling detail. frmSeedling gets launched by an event procedure from a command button on a separate form called frmTransect. The OpenArgs passes the primary key of frmTransect called Transect_OID to frmSeedling. Transect_OID is the link field between frmSeedling and frmSeedlingDetail. I have a Form_Current event procedure in frmSeedling to count the number of unique entries on frmSeedling, and create a custom unique id called Seedling_OID. The tables that are the record source for these forms are linked ODBC tables. Below is the code:
Private Sub Form_Current()
Dim rs As DAO.Recordset
Set rs = Me.RecordsetClone
If Not (rs.EOF) Then
rs.MoveLast
End If
Set rs = Nothing
If IsNull(Seedling_OID) Then
Entry_No = Nz(DCount("Seedling_OID", "rd_Seedling", "Transect_OID = '" & Me.Transect_OID & "'"), 0) + 1
Seedling_OID = Transect_OID & "SD" & Entry_No
End If
End Sub
However, when I launch frmSeedling from frmTransect, I get an error saying I am trying to overwrite the primary key. When I look at the form, this is happening because somehow Access is try to create a new record at the beginning of the form instead of the end of the form, thus thinking that the unique Id I have: created has already been used. Here is a screenshot to show what I mean:
What is curious is that with a separate form called frmDWD with subform frmDWDdetail, I have used this exact setup and it worked fine. Here is the code from frmDWDdetail:
Private Sub Form_Current()
Dim rs As DAO.Recordset
Set rs = Me.RecordsetClone
If Not (rs.EOF) Then
rs.MoveLast
End If
Set rs = Nothing
If IsNull(DWD_OID) Then
DWD_Piece = Nz(DCount("DWD_OID", "rd_DWD", "Transect_OID = '" & Me.Transect_OID & "'"), 0) + 1
DWD_OID = Transect_OID & "W" & DWD_Piece
End If
End Sub
And here is what that looks like:
Notice how in frmDWD, the record being edited is the last record in the form, while in frmSeedling it is trying to edit the first record in the form. I have set all of the data properties exactly the same in both form and subform, and at least to my eye the code looks identical. Any SQL reasons why I am getting this behavior? Any ideas for fixes? Thanks!!
After hours of playing "Which-of-these-things-is-not-like-the-other" I discovered that the issue was in the property sheet of the subform. I switched "Filter on Empty Master" from "Yes" to "No" and that solved my issue.
You are not using the recordset for anything, so try this reduced code:
Private Sub Form_Current()
Dim Entry_No As Long
If IsNull(Me!Seedling_OID.Value) Then
Entry_No = Nz(DCount("*", "rd_Seedling", "Transect_OID = '" & Me!Transect_OID.Value & "'"), 0) + 1
Me!Seedling_OID.Value = Me!Transect_OID.Value & "SD" & Entry_No
End If
End Sub

Subform blocking Recordset.AddNew (Error 3027)

Having a subform displaying the same table used in recordset makes the table read only (error 3027). Getting rid of the subform fixes the problem. Similar setup works in my other form interface.
I tried to delete the subform, which fix the accessibility problem. But that would defeat the original UX purpose.
Option Explicit
Public UnitRS As DAO.Recordset
Public ModelRS As DAO.Recordset
Public NameUnitRecords As String
Public NameModelRecords As String
Public Sub Form_Load()
'Initialization
NameUnitRecords = "tblBatteriesMainRecordsUnits"
NameModelRecords = "tblBatteriesRecordsModels"
End Sub
Private Sub SetUnitRecordsets()
'Set the path to the Battery Records table
Set UnitRS = CurrentDb.OpenRecordset(NameUnitRecords, dbOpenDynaset)
End Sub
Private Sub txtBatteryID_AfterUpdate()
'Set the recordset path for unit records
Call SetUnitRecordsets
'do a findfirst search for the Battery ID, using value from textbox txtBatteryID
UnitRS.FindFirst "[Battery ID]=" & txtBatteryID
'If no matching record, leave the other fields empty
If UnitRS.NoMatch Then
'If there is a matching record, then, grab the model number
Else
'as there is an existing record with model number, run a search on the model number and grab the model info
End If
'close recordset
UnitRS.Close
'check if the button can be enabled
End Sub
Private Sub cmbModelNumber_AfterUpdate()
'Set the recordset path for model records
'do a findfirst search for the Model Number, using value from combobox cmbModelNumber
'If no matching record, leave the other fields empty
If ModelRS.NoMatch Then
'If there is a matching record, then, grab the Model Info
Else
End If
'close recordset
'check if the button can be enabled
End Sub
Private Sub btnSaveAndCLear_Click()
Dim Response, strOldModelNumber
'Set the recordset path for unit records
Call SetUnitRecordsets
'Set the recordset path for model records
'close all related tables, queries and forms
DoCmd.Close acTable, NameUnitRecords, acSaveYes
DoCmd.Close acForm, "frmSubBatteriesMainRecordsUnits", acSaveYes
'If a new model record is required
ModelRS.FindFirst "[Model Number]='" & cmbModelNumber & "'"
If ModelRS.NoMatch Then
'msg box confirm model information
'If user chooses yes
If Response = vbYes Then
'create new model record
'this block could be done with "With...End" format for less error vulerability?
'nah, unless it is repetitively called, it's too much work to inplement just the .addnew part
'requery the two combobox to reflect newest changes
'User chooses no
Else
'popup a message telling the user the battery and model record is not logged, but don't clear the field.
Exit Sub
End If
End If
'need to find the record first, otherwise it will edit the first one
UnitRS.FindFirst "[Battery ID]=" & txtBatteryID
'store the old model number value
strOldModelNumber = UnitRS("Model Number")
'New record
If UnitRS.NoMatch Then
'create a new battery record
UnitRS.AddNew
UnitRS("Battery ID") = Me.txtBatteryID
'If this is an edit on existing record
Else
'if the new value is the same as the old one
If strOldModelNumber = cmbModelNumber Then
'msgbox the same value, no change is done to the database
MsgBox "the data is the same as the old record, no change is made to the record", vbInformation, "Same data"
Exit Sub
Else
'msg box confirm edit
Response = MsgBox("Please confirm edit on existing record: " & Chr(13) & Chr(10) & "BatteryID: " & txtBatteryID & Chr(13) & Chr(10) & "Model Number: " & cmbModelNumber, vbYesNo, "New Model Record Dectected")
'If user chooses yes
If Response = vbYes Then
'goto edit mode
UnitRS.Edit
'if user chooses no
Else
'msgbox notify the user nothing is changed in the database
MsgBox "Battery and Model record not logged, you may retry logging", vbInformation, "Record not logged"
Exit Sub
End If
End If
End If
'both changes the model number and comment field anyway
UnitRS("Model Number") = Me.cmbModelNumber
UnitRS("Comment") = Me.txtComment
'commit update
UnitRS.Update
UnitRS.Close
'clear all flieds
'reset all locks
'requery the sub form to reflect the newest changes
Me.subFrmBatteryRecords.Requery
End Sub
What I would like to achieve is to have a display in the form interface to show the content of the actual record table, so that the user knows what is in the table.
There is nothing wrong with my code. There is a property option in the form object, it's called "record lock", somehow mine had "All Records" selected. By making it "No locks", the accessibility problem is gone.

Using query yes/no field for checkbox record source?

First of all, you start with a form named LoginF. Once you choose your login ID, and password; and log in it takes data from the table LoginIntoT for the login ID you chose, and creates a query with said data using this code:
On Error Resume Next
DoCmd.DeleteObject acQuery, "IsAdminQ"
On Error GoTo Err_LoginBtn_Click
Dim qdef As DAO.QueryDef
Set qdef = CurrentDb.CreateQueryDef("IsAdminQ", _
"SELECT IsAdmin " & _
"FROM LoginInfoT " & _
"WHERE EmployeeID = " & LoginCmBx.Value)
Exit_LoginBtn_Click:
DoCmd.Close acForm, "LoginF", acSaveNo
DoCmd.OpenForm "MenuF"
Exit Sub
Err_LoginBtn_Click:
MsgBox Err.Description
Resume Exit_LoginBtn_Click
From there in that query after you log in is only 1 column and 1 row; meaning one piece of data. This data is a yes/no field which is either Yes or No depending on who you logged in as.
On the form it opens after you click the login button it has a logout button. The logout button brings you to the previous login form, and deletes the query (IsAdminQ).
What I am trying to do is attach a yes/no button on a form to take that data, and output if it's yes or no on the query.
I've tried putting this in it's control source:
=[IsAdminQ].[IsAdmin]
Though what that does is output it as a filled in square instead of a checkmark or empty. I have triple state set as no.
How would I attach the checkbox to the query so if the data says yes, then it's a check mark and if it says no it is an empty box?
I understand you.
"On the form it opens after you click the login button it has a logout button", we call it frmLogout. You shall do this:
Solution I:
frmLogout.RecordSource = "IsAdminQ"
Then for your checkbox named MyCheckbox, we set it this:
Me.MyCheckbox.ControlSource = "IsAdmin"
You cannot use this:
Me.MyCheckbox.ControlSource = "[IsAdminQ].[IsAdmin]" ' <= here it's impossible.
Solution II:
On the form frmLogout without setting IsAdminQ as .RecordSource,
In a Public Module, insert this:
Function GetLoginStateIsAdmin()
'
Dim rst As DAO.Recordset
Set rst = CurrentDb.OpenRecordSet("IsAdminQ")
GetLoginStateIsAdmin = Nz(rst(0), False)
Set rst = Nothing
'
End Function
Then in the private module of any form, as frmLogout:
Private Sub Form_Open(Cancel As Integer)
'
Me.MyCheckbox.Value = GetLoginStateIsAdmin()
'
End Sub
Set it in addition in design mode:
Me.MyCheckbox.TripleState = false
And check also if the query IsAdminQ has been successfully created in the login step. And open it in Access Navigation Pane by double-clicking on it. And see the value of the query.

More info when you click a field in a subform table in pop up form

I am trying to get a window to popup when you click that small box beside a field in my subform table, and then click the button "more info". This form would pop up with all the extra information about that record; in text boxes so you can edit it. On this form it pops out, it only has a button that has back and when you click back, it saves all the changes made in the text boxes in the record you were viewing more information about... and updates it. It then brings back up the previous form, and updates the subform on that form so it shows all changes made to the table. I am not looking for the More Info button to show more info for all records. I am looking for it to bring up more info for that one record you selected only, and to get more info on another, you must click back and select a new one. How would I go about setting up the More Info button so it pops up the new form with the information?
For an example of what I mean by pressing the more info button, here is a picture:
In the picture, it is a table. Clicking that small space highlights the whole record, and you are able to push DEL on your keyboard to delete the record (though that doesn't matter in the case, just pointing out it's a table.)
Code I have in my More Info button:
Private Sub CustomerMoreInfoBtn_Click()
On Error Resume Next
DoCmd.Close acForm, "CustomerInfoF"
DoCmd.DeleteObject acQuery, "CustomerMoreInfoQ"
On Error GoTo Err_CustomerMoreInfoBtn_Click
Dim qdef As DAO.QueryDef
Set qdef = CurrentDb.CreateQueryDef("CustomerMoreInfoQ", _
"SELECT * " & _
"FROM CustomersT " & _
"WHERE CustomerID = " & txtCustID.Value)
DoCmd.OpenForm "CustomerInfoF"
Exit_CustomerMoreInfoBtn_Click:
Exit Sub
Err_CustomerMoreInfoBtn_Click:
MsgBox Err.Description
Resume Exit_CustomerMoreInfoBtn_Click
End Sub
Code in the form it self:
Private Sub Form_Close()
On Error Resume Next
DoCmd.DeleteObject acQuery, "CustomerMoreInfoQ"
End Sub
In the below example your button is named cmdMoreInfo. When you click on the button it creates the MyCustDetail query based on the value of the customer id. Then it opens the frm_CustDetailEdit form. Set the forms Record Source to MyCustDetail.
In order to be able to create the query it must not already exist. Therefore, I check beforehand and delete it if it exists. Also, when closing the main form it also will delete the query since it would no longer be needed anyway.
Private Sub cmdMoreInfo_Click()
On Error Resume Next
DoCmd.Close acForm, "frm_CustDetailEdit"
DoCmd.DeleteObject acQuery, "MyCustDetail"
On Error GoTo Err_cmdMoreInfo_Click
Dim qdef As DAO.QueryDef
Set qdef = CurrentDb.CreateQueryDef("MyCustDetail", _
"SELECT * " & _
"FROM tbl_Customer " & _
"WHERE CustomerID = " & txtCustID.value)
DoCmd.OpenForm "frm_CustDetailEdit"
Exit_cmdMoreInfo_Click:
Exit Sub
Err_cmdMoreInfo_Click:
MsgBox Err.Description
Resume Exit_cmdMoreInfo_Click
End Sub
Private Sub Form_Close()
On Error Resume Next
DoCmd.DeleteObject acQuery, "MyCustDetail"
End Sub