Subform blocking Recordset.AddNew (Error 3027) - vba

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.

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

Dealing with edited fields left in blank on subform

I am having a problem when an user decides to "clear/delete" a line entered on the form during the first update.
I have a form with a subform to give the user the possibility of multiple entries, however, after the user has first edited any field on the subform the respective record gets dirty and recorded, so if the user decides to not use anymore that record and deletes all the info for that line, that line which now is all blank still being recorded, and this is giving me blank lines in my data (The TransactionID and ID are not shown to the user on the form, I added it for the sake of this post).
This can also accidentally happen if on the "new" line, which is not dirty, the user hits any key by mistake, and even deleting it straight away, the record gets dirty for good, so it ends being recorded completely in blank.
I couldn't find a way to avoid recording this blank lines.
I would like to know if it is possible to have buttons to "add a new line" and "delete a line", just like, on the last line it would have a "+" to add new line (so the "new not dirty" line wouldn't appear automatically), and on the precedent lines would have a "-" to remove that specific record (this is just an idea, it can be in any kind of way).
Is that achievable? If it is not, is there a way to simply avoid blank lines to be saved? Like, saving jumping those blank lines, just like in a loop?
More information added for the sake of better explanation.
I didn't include the code before as there is no code behind this, this is just access natural recording set. I am not using DAO Record Set nor SQL to "save" the records (just to delete from table in a discard event), I am using normal bounded controls.
The modifications I did were on Parent form to always stay on the Current Record (Cycle) and On Load event it always goes to new record. Thus, the buttons Save and Cancel, are used to confirm or discard the new record created on the Parent form, because, as I have previously said, the subform is used to enter multiple records under the same record on the Parent Table, in other words, Relationship One-To-Many, therefore the button "Close" holds a simple code for deleting the last entry on the Parent Table (as it still on the current record) and as the tables have "Delete Cascade Relationship" all the information entered into the subform/Child Table get automatically deleted, so I just had to handle the data inserted into the Parent Table, whereas, the save button is used to get the confirmation of saving from the user, and make sure that the user will fill all the fields prior to save.
That is pretty much the code on this form, which is below.
But before the code, I would like to reinforce that, I have made use of the "BeforeUpdate" event, which works fine, it makes the user to add all the fields before opening a new record on the subform, but let's suppose that the user enters 10 records on the subform, then the user decides to "Delete" one of the records (which in this case is not the last one, so he cannot just press "ESC" and I cannot just run "Me.Undo"), so if the user clears the fields (just like in the images I have provided, as I don't know how to delete a record in this scenario yet), the before update event gets triggered and doesn't allow the user to do anything while he/she fills in the blank fields, not even to click in "Cancel". Therefore, in my "Save" button I check all the fields and if one of them is "dirty" them the user must fill all the subsequent fields, however, if all the fields are "empty", the user is still able to save.
In the beginning I thought that those "blank lines" wouldn't happen so often and I would have to clean them sporadically, but it is unbelievable, the amount of blank lines that have been created by the users. I have to clean them almost every week.
I really would like a way to create "+ add line" and "- delete line" buttons, because if the user wants to delete a line he just click on "-" button, the same for add new line.
Again, I appreciate the help. I am really struggling on this. Thanks all.
Private Sub btnSave_Click()
If IsNull(Me.TransactionDate) _
And IsNull(Me.SupplierPayee) _
And IsNull(Me.TotalReceipt) Then
MsgBox "Form in blank!", vbInformation + vbOKOnly, "Saving..."
Exit Sub
End If
If IsNull(Me.TransactionDate) _
Or IsNull(Me.SupplierPayee) _
Or IsNull(Me.TotalReceipt) Then
MsgBox "Please fill in all the fields", vbCritical + vbOKOnly, "Transaction not Saved"
Exit Sub
End If
Me![subfrmPCHeaderDetail].SetFocus
DoCmd.GoToRecord , , acFirst
If IsNull(Me.TransactionDate) _
Or IsNull(Me.SupplierPayee) _
Or IsNull(Me.TotalReceipt) _
Or IsNull(Form_frmPCHeaderDetail.Item) _
Or IsNull(Form_frmPCHeaderDetail.ItemAmount) _
Or IsNull(Form_frmPCHeaderDetail.VATRate) _
Or IsNull(Form_frmPCHeaderDetail.DescriptionPurpose) Then
MsgBox "Please fill in all the fields", vbCritical + vbOKOnly, "Transaction not Saved"
Exit Sub
ElseIf Form_frmPCHeaderDetail.ItemAmount = "0" Then
MsgBox "Amount cannot be €0.00", vbCritical + vbOKOnly, "Transaction not Saved"
Form_frmPCHeaderDetail.ItemAmount.SetFocus
Exit Sub
End If
If Not Me.TotalReceipt.Value = Form_frmPCHeaderDetail.GrossTotal _
Or IsNull(Me.TotalReceipt) Then
MsgBox "Please check the Receipt Amount Details as ""Gross Total"" and ""Receipt Total"" are not matching"
Exit Sub
End If
If Me.ReceiptScanned.AttachmentCount = 0 Then
MsgBox "Please scan the receipt and add it to this transaction", vbInformation + vbOKOnly, "Add the Receipt"
Me.AddReceiptScanned.SetFocus
Exit Sub
End If
If Me.TransactionDate > Date Then
MsgBox "Future Transaction Date not allowed", vbCritical + vbOKOnly, "Transaction not Saved"
Me.TransactionDate.SetFocus
Exit Sub
Else
Dim Result As Long
Result = MsgBox("Saving Transaction!" & vbNewLine & vbNewLine & _
"Attention!" & vbNewLine & vbNewLine & _
"You will not be able to delete nor modify any detail of this transaction!" & vbNewLine & vbNewLine & _
"Are you sure you would like to save this transaction?" & vbNewLine & vbNewLine & _
"Click ""YES"" to save, or click ""NO"" to return.", vbExclamation + vbYesNo, "Save Transaction?")
If Result = 6 Then
DoCmd.Save
DoCmd.Requery
Form_frmPCHeader.TransactionDate.SetFocus
DoCmd.GoToRecord , , acNewRec
MsgBox "Transaction added successfully to the Petty Cash Register", _
vbInformation + vbOKOnly, "Petty Cash transaction added"
End If
End If
End Sub
Private Sub btnClose_Click()
Dim LastID As Long
Dim Result As Long
If IsNull(TransactionID) Then
DoCmd.Close
Exit Sub
End If
LastID = TransactionID
MsgBox("Closing without saving!" & vbNewLine & vbNewLine & _
"If you want to discard the entries, click ""YES"", or click ""NO"" to return.", vbCritical + vbYesNo, "Discard Entries?")
If Result = 6 Then
Application.Echo False
Me.Painting = False
DoCmd.SetWarnings False
Me.TransactionDate = "31/12/2099"
Me.SupplierPayee = "Discarded"
Me.TotalReceipt = 0
DoCmd.Save
DoCmd.Requery
DoCmd.RunSQL "DELETE * FROM tblPCHeader WHERE TransactionID = " & LastID & ""
DoCmd.Close
Application.Echo True
DoCmd.SetWarnings True
End If
End Sub
Private Sub Form_Load()
DoCmd.GoToRecord , , acNewRec
End Sub
For a Delete (-) button, consider code:
DoCmd.SetWarnings False
DoCmd.RunCommand acCmdDeleteRecord
DoCmd.SetWarnings True
For Add (+) button, just move to new record row with code you already know.
Docmd.GoToRecord , , acNewRec

MS Access vba save button error 3021

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

If an Access table has a new record selected, It displays (New). How do I get my code to recognize the (New) value?

I Want to be able to let the end user of the database know when they are on a new record, but I do not want to display the actual id's. I just want the text box to display "New" when it is a new record.
I have two buttons one the selects the previous record and the other that selects the next record. The next record button has the code that I am trying to get to work.
Private Sub Command25_Click()
On Error GoTo Command25_Click_Err
On Error Resume Next
DoCmd.GoToRecord , "", acNext
' I wrote this if statment to capture the (New)
If frmQuote_QuoteID.Value = " " Then
frmQuote_QuoteNumber.Value = "NEW"
End If
If (MacroError <> 0) Then
Beep
MsgBox MacroError.Description, vbOKOnly, ""
End If
Command25_Click_Exit:
Exit Sub
Command25_Click_Err:
MsgBox Error$
Resume Command25_Click_Exit
End Sub
I have also tried if frmQuote_QuoteID.value = "(New)" Then
I am trying to get this to the point where the form can display New based on an empty primary key field, but if it is not a new record then I don't want anything displayed
You need to use
if me.NewRecord then

Me.Dirty moves currently selected record

I have an Access 2007 form that's giving me some headache.
I have a list of records that start as a combo box to pick a company and then a series of checkboxes to indicate the company's role.
If the user needs to add a new company, they'd pick the company name from the combo box, and then click the checkbox that indicates what role the company is playing. When the checkbox is checked, the form follows the following code to show a popup that captures additional information:
Private Sub chkOperationsAndMaintenance_AfterUpdate()
'DoCmd.RunCommand acCmdSaveRecord'
If Me.Dirty Then Me.Dirty = False
If chkOperationsAndMaintenance.Value = True Then
DoCmd.OpenForm "OmPopupForm", , , "CompanyProjectId = " & Me.CompanyProjectID, , acDialog
Me.Requery
End If
End Sub
This code will save the record on the CompanyProject table to create the row. It then pulls the CompanyProjectID (PK of the CompanyProject table) so it knows which ID to feed the popup.
The issue I'm having is that on the Me.Dirty line (and also the above commented-out acCmdSaveRecord), the entire form saves and refreshes, moving the selected row to the first record (in this case, "Gamesa") rather than the newly entered record. So the ensuing popup is fed the CompanyProjectID of the first record rather than the newly-entered record, and it also reads the "checked" state of the checkbox from the first record rather than the one upon which the user is working.
I doctored the code to look like this:
Private Sub chkOperationsAndMaintenance_AfterUpdate()
Dim CPID As Long
Dim CID As Long
Dim rs As Recordset
Set rs = Me.RecordsetClone
CID = Me.CompanyID
'DoCmd.RunCommand acCmdSaveRecord'
If Me.Dirty Then Me.Dirty = False
CPID = DLookup("MAX(CompanyProjectID)", "CompanyProject", "cpProjectID = " & Me.cpProjectID & _
" AND CompanyID = " & CID)
rs.Find "[CompanyProjectID] = " & CPID
Me.Bookmark = rs.Bookmark
If chkOperationsAndMaintenance.Value = True Then
DoCmd.OpenForm "OmPopupForm", , , "CompanyProjectId = " & Me.CompanyProjectID, , acDialog
Me.Requery
End If
End Sub
The idea with these alterations is that we'll get the FK from the parent form ("cpProjectID") and the CompanyID of the selection from the combobox, then save the record. Once the record is saved, we have the CompanyProjectID already stored, so then the rs.Find and Me.Bookmark lines will then select the record that matches that CompanyProjectID.
This sometimes works, and sometimes doesn't. Generally doesn't. In this case, I refreshed the parent form and the current form and attempted to add a new company and then click the "Owner" checkbox (which uses the same code as above, just a different checkbox ID) to see the Bookmark on the wrong row:
At this point, I'm not sure what to do. If I don't save the record (via acCmdSaveRecord or Me.Dirty=False) then I don't have a CompanyProjectID to send as the input parameter for the ensuing popup, but if I do save the record, then the form changes the record and the wrong parameter is sent and the wrong checkbox's checked state is read. I can't just use an arbitrary index to work off of (such as acNewRec), as the user might need to edit an existing row instead of adding a new one.
I tried the method in this post already: MS Access how to Update current row, move to next record, not first, but the find/bookmark isn't consistently working.
EDIT (12/15/2014)
I ended up going with the following VBA code:
Private Sub chkOperationsAndMaintenance_AfterUpdate()
Dim CPID As Long
Dim CID As Long
Dim rs As Recordset
Set rs = Me.RecordsetClone
CID = Me.CompanyID
If Me.Dirty Then Me.Dirty = False
CPID = DLookup("MAX(CompanyProjectID)", "CompanyProject", "cpProjectID = " & Me.cpProjectID & _
" AND CompanyID = " & CID)
Do While Me.CompanyProjectID <> CPID
If Me.CurrentRecord < Me.Recordset.RecordCount Then
DoCmd.GoToRecord , , acNext
Else
DoCmd.GoToRecord , , acFirst
End If
Loop
If chkOperationsAndMaintenance.Value = True Then
DoCmd.OpenForm "OmPopupForm", , , "CompanyProjectId = " & Me.CompanyProjectID, , acDialog
Me.Requery
End If
End Sub
I feel like there has to be a better answer for this, but this is appearing to work right now. I have my end user/lab rat testing it at the moment, so I'll mark this complete if this pans out. I still think there has to be a better solution for this, but at the moment, this solution is able to capture the requisite ID to pass to the popup, and it selects the appropriate row after this ID is captured.
Have you considered saving the current position - like Me.CurrentRecord, then after the save reposition to the desired record. As an example, you could add a 'Before' or 'After' Insert to save the position, then later reposition. See the following:
Option Compare Database
Option Explicit
Dim lCurRec As Long
Private Sub Form_AfterInsert()
lCurRec = Me.CurrentRecord
Debug.Print "After Insert, Current: " & Me.CurrentRecord
End Sub
<<< YOUR SUB>>>
DoCmd.GoToRecord acDataForm, Me.Name, acGoTo, lCurRec
This code will work when adding a new record as well as when editing an existing record:
Private Sub chkOperationsAndMaintenance_AfterUpdate()
Dim c As Long
Me.Dirty = False
If chkOperationsAndMaintenance Then
DoCmd.OpenForm "OmPopupForm", , , "CompanyProjectId = " & Me.CompanyProjectID, , acDialog
c = Me.CurrentRecord
Me.Requery
DoCmd.GoToRecord , , acGoTo, c
End If
End Sub
I am guessing that your popup form does things that are specific to Operations & Maintenance, and does not change the values of any of the checkboxes on the main form. If this is the case, you don't need a Requery at all, and the code can be simplified:
Private Sub chkOperationsAndMaintenance_AfterUpdate()
Me.Dirty = False
If chkOperationsAndMaintenance Then
DoCmd.OpenForm "OmPopupForm", , , "CompanyProjectId = " & Me.CompanyProjectID, , acDialog
End If
End Sub