How to fix “Could not update; currently locked” caused by using DAO.Recordset2.Edit while a form is being edited - ms-access

DAO.Recordset2.Edit is causing a “Could not update; currently locked” error.
I want to save the edited form, update the record and refresh the form.
But all attempts to save the form fail to fix the error. They fail to clear the record lock. Closing the form clears the lock, but that is messy, looking for a better way.
Steps:
typed data into a form.
Clicked a button that triggers:
' saves the form but does not clear the record lock
If Me.Dirty Then
Me.Dirty = False
End If
click another button that triggers adding a photo:
Private Sub EndPhotoTaking_Click()
Dim attachmentField As DAO.Field2
Dim photoItemRecordSet As DAO.Recordset2
With [Forms]![Inspection - All sub sections].Form
Set photoItemRecordSet = .Recordset
End With
Set attachmentField = photoItemRecordSet("Photo")
Set photoItemAttachment = attachmentField.Value
'---- error “Could not update; currently locked” on line below
photoItemRecordSet.Edit
Then I get the error “Could not update; currently locked”.
I am using VBA to add a picture into the record, but it is not working. It does not work if the record has been changed in any way.
It is running the Line of code "Me.Dirty = False", so this saves the record. But then I get the error. I only get the error if I enter data into the from.
The form that I enter data into contains a subform, would that have an impact? I suspect it does because I have another form that does not have a subform and it works just fine with the same code. Is there anything that would cause "Me.Dirty = False" to fail to clear the lock.
I have also tried "DoCmd.Save acForm, formName", but that did not help.
The table is sharepoint list style.
Edit
Added extra code to show origin of photoItemRecordSet.Edit
updated title and description. Added details to say saving the form is not clearing the record lock.

Workaround: close the form. Update the record. Reopen the form.
This works but is less than ideal.

Use the RecordsetClone:
Set photoItemRecordSet = .RecordsetClone
photoItemRecordSet.Bookmark = .Bookmark

I was having a similar problem, the record was saved and a requery didn't help. I was finally able to set the form RecordSource to the recordsource that I originally had it set to again, navigate to the record I was on and then the add photo worked.
I put this code in the Error Handler;
If Err.Number = 3218 Then
rstEmployee.Close
Set rstEmployee = Nothing
Me.RecordSource = "qryPersonnelData"
Set rstEmployee = db.OpenRecordset(strSQL)
DoCmd.SearchForRecord , "", acFirst, "[ID] = " & thisID
Resume
I hope this helps someone.

Related

Use VBA to set checkbox in table not form

In the code for a SAVE button, I want to save the record, and mark a checkbox in a specific table which is not the same table as the one where I am saving a record. It's basically like a checklist table to indicate something has been done. I know -1 represents a check mark so I have tried:
Me.tblProjectCompletion.Done = -1
and I have tried it without the Me.
I also tried putting it into a form first to save it into a table but that caused other problems. If I can save the check mark in the table I want for the projectID it would be best, so I have also tried:
if ProjectCompletion.ProjectCompID = Project.ProjectID then tblProjectCompletion.Done = -1
But code like that is not working either. Any suggestions?
Currentdb.Execute
Update ProjectCompletion
Set [ProjectCompletion].[Done] = -1
WHERE [ProjectCompletion].[ProjectCompID] = [Project].[ProjectID]
I've tried this code with and without brackets and quotes, and a semicolon at the end. With this pattern it stops on Execute and says argument not optional. The following code works. I just need to add the checking of the check box.
Private Sub Save_Record_Click()
DoCmd.RunCommand acCmdSaveRecord
If Me.Dirty Then
Me.Dirty = False
End If
End Sub
ok, so I am guessing this is a form module. The problem is that it has no clue what record you are looking to update so you need to use the CurrentRecord property
CurrentDb.Execute "UPDATE ProjectCompletion SET [ProjectCompletion].[Done] = -1 WHERE [ProjectCompletion].[ProjectCompID] = " & Me.Recordset.Fields("ProjectID").Value

Access Set Unbound Form to Current Record

First post so hoping this makes some sense. I’m adding data to tables from unbound text boxes using a command button and the .AddNew method. The form itself is still bound at this point. FYI, I’m using unbound boxes as the UI needed for junction tbls was not an option. I’m able to add the data to the tables without a problem. The issue is that after adding a record, the form’s current record stays on the same record as before the addition but I want it to be on the new record. I’m using “rst.Bookmark = rst.LastModified” to set the cursor to the new record in the table and seems to work as I can return values for the new record ... but this does not affect the form. I’ve tried working with “Requery” and “CurrentRecord” but neither resolves the issue.
In summary, after adding a new record to a table I want the form’s current record to reference this new record. For example, if I am on record 12 in the form and add a new record bringing the total records to 25 -- I want the form to be on record "25 of 25" not on "12 of 25". Can you tell me how to do this? Thanks for your help!
With rst
.AddNew
!Event = Forms!MainForm!Event
!DonorName = Forms!MainForm!DonorName
!EnvNo = Forms!MainForm!EnvNo
.Update
.MoveLast
.MoveFirst
End With
rst.Bookmark = rst.LastModified
''' Record count total - show on form - confirmed/same as Access Navigation display
Forms!MainForm!txtTotalRec.Value = rst.RecordCount
''' Current Record - show on form - confirmed/same as Access Navigation display
lngCurrent = Forms!MainForm.CurrentRecord
Forms!MainForm!txtCurrRec.Value = lngCurrent
as per your trial. simply perform a DoCmd.GoToRecord acActiveDataObject, , acLast after a me.requery to update the recordset.
it should work, because the addnew method does not require movelast action. you can try refreshing the form with set me.recordset = me.recordset

"Operation not supported in transactions" While Copy/Paste Records in Access

When copy / pasting cells from Excel into an Access Subform, when no parent record has been created, will result in an error - as expected.
The problem, is that after this error occurs access gets locked in a state where all subsequent data that is entered results in an Operation not supported in transactions error. When you open the table, you can see that the newly added data is not yet in the table - so it appears as if Access is in fact engaged in some sort of transaction.
I have tried hitting 'Save' .. 'Refresh' .. and even adding an AfterInsert event to force a commitTrans() but have had no luck - and Access claims there is no transaction underway
There is no error if the records are entered manually. The problem only seems to occur when pasting records. I'm guessing that Access is creating a transaction to handle the multiple record operations and is not properly recovering from the error.
I can remove the 'Required' flag and it will work - but then you have orphan records. I was thinking that perhaps an After Insert Data Macro could be used to add a new Batch with a default batch Name, and auto-fill the new BatchID into the Items table. I am not certain how that would be done however.
I did try to remove the 'Required' flag and trap for the error using a Before Change Data Macro - but while it cut down on the errors - it still produced the same Operation not supported in transactions error.
I have looked up the error on the Microsoft Knowledgebase, but did not find anything specific to my circumstances. I searched stackoverflow for the error message and found nothing.
I created a new database and was able to replicate the issue.
Steps to Replicate
Set up the Database
Create a new ACCDB database in Access 2010
Create a Table called 'Batches', with the following fields:
BatchID (AutoNumber) (Primary Key)
BatchName (Text)
Create a Table called 'Items', with the following fields:
RecordID (AutoNumber) (Primary Key)
BatchID (Long Integer)
Set Required attribute to True
Data - Text
Create a Relationship, linking Batches.BatchID to Items.BatchID
Include all Records from Batches, and matching records from Items
Enforce Referential Integrity
Cascade Updates / Deletes
Create a Form called 'Form'
Set the Recordsource to Batches
Add in the BatchID and Batch name Textboxes
Add in a SubForm/SubReport Control
Set Source Object to "Table.Items"
Set Link Master Fields to "BatchID"
Set Link Child Fields to "BatchID"
Set "Filter On Empty Master" = Yes
Create sample data (Using the Form)
Create a New Record in Batches.
Set BatchName = "Test"
Create a New Record in Items.
Reference the Batch Record.
Set Data = "Test"
As you can see, by hand this works fine.
Copy and Paste Data From Excel
In Excel
From A1-A10 enter one letter per cell running down: A,B,C,D,E,F,G,H,I,J
Highlight the cells A1-A10
Copy (Control+C)
In Access, using the Form:
Add a New Batch Record. It should say "(New)" in BatchID TextBox
Do NOT enter a Batch Name
In the Sub-Form, click the record selector (*) for the new record to select the entire row. Type Control+V to paste.
Click OK for "You must enter a value in the 'Data.BatchID' field. error
This error may repeat. Keep Clicking OK
If it asks "Do you want to suppress further error messages..." answer Yes
Click OK for the "Records that Microsoft Access was unable to paste have been inserted into a new table called 'Paste Errors.' notification
Fill in the Batch Name Textbox with "TestName"
Try to gracefully recover. Hit Escape. Change the Record.
At this point - you should see the BatchID entered, the Batch Name, and the New Data. Everything appears to be working as expected. If you try to refresh or navigate to another batch record - you will get the error Operation not supported in transactions. Access will continue to display this error message until we close and reopen the form. The data you pasted will not have made it into the database.
Normally someone with a bit of tech savvy will realize something isn't going well and close out of the database and re-open ... but unfortunately I have users that play "whack-a-mole" with any popup boxes and then try to continue on - so I'd like to make things as bulletproof as possible.
Desired Solution
I'd like a workaround to the problem, that won't eventually lead to other quirks with access, duplicate values, etc.
In my own experience, using VBA to 'fix-up' keys isn't reliable. Data macros seem to be a lot more reliable - but they can be tricky to set up - they aren't very mainstream yet (I'd say there should be a ms-access-data-macros tag on stackoverflow but there isn't yet)
Suggested workaround:
In the [Batches] table, set the Required property of the [BatchName] field to Yes.
Change the Tab Stop property of the [BatchID] text box to "No". That will give the [BatchName] text box the default focus when the form opens.
Have the On Current event of the form give the [BatchName] text box the focus for new records (IsNull(Me.BatchID) = True).
Make the form dirty when the [BatchName] text box loses focus.
Option Compare Database
Option Explicit
Dim DeletePending As Boolean
Private Sub Form_Load()
DeletePending = False
Me.ItemSubForm.Enabled = False ' Disable Subform by default
End Sub
Private Sub Form_Current()
If IsNull(Me.BatchID) Then
Me.BatchName.SetFocus
' Disable Subform if BatchID is NULL
Me.ItemSubForm.Enabled = False
Else
' Enable SubForm Otherwise
Me.ItemSubForm.Enabled = False
End If
End Sub
Private Sub Form_BeforeDelConfirm(Cancel As Integer, Response As Integer)
DeletePending = True
End Sub
Private Sub Form_AfterDelConfirm(Status As Integer)
DeletePending = False
End Sub
Private Sub BatchName_LostFocus()
If IsNull(Me.BatchID) And Not DeletePending Then
Me.Dirty = True
End If
End Sub
When the user clicks on the subform (and off the [BatchName] text box) they make the form dirty and BatchID gets a value. Then they can paste and they don't get the "You must enter a value..." message for [BatchID]. If they haven't entered a [BatchName] value they now get prompted for it (because it is now Required), but at least they can gracefully recover from that.
Updated 2013-11-09 18:40 UTC:
In playing around with this a bit more I discovered two quirks:
If you deleted the last parent record Access would do it and then immediately create another new one in its place.
If you navigated into the "new" parent record and then immediately backed out (e.g., via the PageDown and PageUp keys) Access would create a new record and then leave you on that record with the form dirty. Hitting Esc and then moving off the "new" record worked, and it didn't cause any errors, but it certainly could be confusing to the user.
I have updated the answer to try and address these issues. I added a bit more VBA code to track "DeletePending" status. I also added the requirement that [BatchName] be set to Required = Yes in the [Batches] table. The latter makes it slightly more intuitive (albeit slightly more annoying) if the user move into the "new" record and then immediately moves back out again.
I struggled with this for a long until I finally understood what is happening to cause this error. It would take an article of considerable length to go into the details rather than a blog response to explain it all. If anyone is interested they can leave a contact method and I will contact them and explain it in detail.
However, for those who want to figure this out, I can save you a lot of time by giving you the idea behind the issue:
When you are performing a data transaction in a bound sub-form, you cannot reference other objects. The internal code of the transaction process does not allow this. For example, if you have code in the Form_BeforeUpdate event that attempts to open another form while in the midst of a sub-form data transaction, you will get error 3246. You can have code that creates variables, set values, references the sub-form controls, etc. but you cannot go out to another object.
This makes sense if you think about it. Who knows what the user or code might do once it gets itself into another form or object. It may never come back or get involved in other errors that leave the transaction hanging. That's why the transaction must complete first.
This means that you must trap and dismiss error 2115 that is caused when a user tries to click on an area outside the sub-form while in the midst of a transaction. This usually occurs during large copy and paste where the user becomes inpatient or starts to proceed to another record while still in the midst of the sub-form transaction.
I know that this is an old storry, I was also strugling with this.
My solution was to re-desing the process so the user closes the form receiving the data in order to save the records inserted. Is nor elegant but efficient and saved me from guessing each and every event which could occure.
to avoid the undesired internal transaction is enough to
code the sub-form Error event with:
Private Sub Form_Error(DataErr As Integer, Response As Integer)
Response = acDataErrContinue
End Sub
A general approach for intercepting sub-forms events is
' parent form code
' ---------------------------------------------------
Private WithEvents subFormObj As [Form_Sottomaschera items]
Private Sub Form_Open(Cancel As Integer)
Set subFormObj = Me.Sottomaschera_items.Form
End Subcode here
' asynchronous way
Private Sub subFormObj_isInserting()
Me.batchName = vbNullString ' this resolves the new ID issue
End Sub
' synchronous way
Public Sub subFormInserting()
Me.batchName = vbNullString
End Sub
' sub-form code
' ---------------------------------------------------
Public Event isInserting() ' for asynchronous way
Dim parentFormObj As Form_Maschera1 ' for synchronous way
Private Sub Form_BeforeInsert(Cancel As Integer)
parentFormObj.subFormInserting
RaiseEvent isInserting
' Cancel = True
End Sub
Private Sub Form_Error(DataErr As Integer, Response As Integer)
Response = acDataErrContinue
End Sub
Private Sub Form_Open(Cancel As Integer)
Set parentFormObj = Me.Parent
End Sub
where [Maschera1] is the main form and [Sottomaschera items] the sub-form.
Unfortunately it doesn't resolve the paste issue.
To definitely resolve issue you need to save parent record + a SetFocus trick, either synchronous or asynchronous:
Private Sub subFormObj_isInserting()
Me.batchName = vbNullString
DoCmd.RunCommand acCmdSaveRecord
' DoEvents
Me.batchName.SetFocus
End Sub
Public Sub subFormInserting()
Me.batchName = vbNullString
DoCmd.RunCommand acCmdSaveRecord
' DoEvents
Me.batchName.SetFocus
End Sub
I don't understand what exactly do you want to achive, so this answer may be inadequate.
You can
set your subform property .Visible = False when Me.NewRecord = True to prevent entering data into it
force saving record of the main form to the table after adding Batch name by setting .Dirty = False in After Update event triggered by pressing Enter. It allows also to avoid not saving records of a subsform to a table after adding few records to a main form in some databases, at least with dynamical subform .Recordsource.
set your subform property .Visible = True
The code below works for Form View and perhaps should be extended (develop) somehow for other Views.
Set .Tag of the subform Child and all other controls you want to hide / show to "a".
Private Sub Form_Current()
If Me.CurrentView = 1 Then
If Me.NewRecord = True Then
ShowControls False
ElseIf Me![Items subform Child].Visible = False Then
ShowControls True
End If
End If
End Sub
Private Sub BatchName_Text_AfterUpdate()
Dim NewRecordx As Boolean
If Me![Items subform Child].Visible = False And Me.CurrentView = 1 Then ShowControls True
NewRecordx = Me.NewRecord
If Me.Dirty Then Me.Dirty = False 'save the new record to the table
If Me.CurrentView = 1 And NewRecordx Then Me![Items subform Child].Form.Requery
End Sub
Private Sub ShowControls(bVisible As Boolean)
Dim ctl As Control
For Each ctl In Me.Controls
If ctl.Tag = "a" Then ctl.Visible = bVisible
Next ctl
End Sub
I reported this as a bug through Microsoft Premier Support a number of years ago with a concise standalone repro case. It appears this was finally resolved in October 2021 with KB5001978.

After creating a new record, I can't get my form to display it in Access 2010

StackOverflow. I have an issue I've been grappling with for too long. Currently, I have a form that displays consortium data, with a button that says "Create new consortium." When I click it, it shows a pop up window that allows you to enter the name, as displayed in the image below. Almost everything works fine, except I can't get it to display the new record after I create it. See my code below--you'll get a sense of what I'm trying to do.
By the way, if it matters, the "Consortium name" combo box allows you to select records. I don't see how it could throw my update off, but I thought I should include that information.
Here's the form:
Here's the code:
Interestingly, StackOverflow doesn't seem to indent the code properly after making an edit--I was able to do it successfully with a new test post. Oh well. I'm a noob to this as well:
Private Sub CreateConsortiumButton_Click()
If IsNull(Me.ConsortiumNameText) Or Me.ConsortiumNameText = "" Then ''insert better validation here
MsgBox "Please enter a valid consortium name."
Else
'' Create a new Consortium record with the ConsortiumNameText field on the popup window
Dim rst As DAO.Recordset
Set rst = CurrentDb.OpenRecordset("Consortium")
rst.AddNew
rst("Consortium name").Value = Me.ConsortiumNameText
ConsortiumID = rst("ConsortiumID").Value
rst.Update
'' Now, create a corresponding record in the ResearchContributions table
Set rst = CurrentDb.OpenRecordset("ResearchContributions")
rst.AddNew
rst("ConsortiumID").Value = ConsortiumID
rst.Update
MsgBox "Consortium " & Me.ConsortiumNameText & " successfully created."
'' Change the form's combo box to reflect the new record
Forms!Main!NavigationSubform!ConsortiumNameCombobox = Me.ConsortiumNameText
'' Close popup
DoCmd.Close
'' Attempt to set the record on the main form to the newly created one - NOT WORKING!
DoCmd.OpenForm "Main", acNormal, , "[ConsortiumID] = " & ConsortiumID
End If
End Sub
I think part of the issue may have to do with the fact that I've placed the "Consortium Form" form, which is based around a tab control, into the "Main" form. Perhaps because I'm trying to open the Main form with a certain ConsortiumID, that ConsortiumID doesn't apply to Consortium Form. It appears as though I can't link the master with its child, but this hasn't been necessary in the past. Please forgive me if the concepts sound vague in confused, for it is only because the concepts are vague and confused as of yet in my mind--this is my first Access project. Anyone have any tips?
Okay, I think I figured it out. I've added the following code:
DoCmd.OpenForm "Main"
DoCmd.Requery
DoCmd.SearchForRecord , , acFirst, "[ConsortiumID] = " & ConsortiumID
First, open the form, then requery in able to register the fact that the new item is in the database, then use SearchForRecord to open it up. Neato. Thanks, everyone.

Started to get a Run-Time error '3159'; Not a Valid bookmark all of a sudden in Access 2007

I am really new to programming access. I converted an old 97 access db to access 2007 and it has been working fine for awhile. Today I go into it and was snooping around in design view and looking at the code. I didn't change anything. Just looked. When I went to run the code I kept getting the "Not a valid bookmark" error. Just to be sure I opened up the old program I converted and all the code is the same the line that is giving me the problem is the
Me.Bookmark = pos
The following is the whole routine.
Thanks in advance.
Private Sub ProductID_AfterUpdate()
Dim pos As Variant
Me![UnitPrice] = Me![ProductID].Column(2)
Me![ProductName] = Me![ProductID].Column(1)
Me![GLAcct] = Me![ProductID].Column(3)
DoCmd.DoMenuItem acFormBar, acRecordsMenu, acSaveRecord, , acMenuVer70
pos = Me.Bookmark
Me.Requery
Me.Bookmark = pos
End Sub
Edit: I already tried compact and repair.
You will need to change this code as it is out of date for quite some time. I am not sure why it worked in the first place, because the bookmarks will have changed after updating, saving and requerying. I have commented the requery line, because it does not seem to serve a useful purpose, if you wish to update the combo, requery that. If you wish to find a record after an action, save the unique ID to a variable and find it. DoMenuItem is deprecated, you can use RunCommand instead, in this case, however, Me.Dirty=false will save.
Private Sub ProductID_AfterUpdate()
Dim pos As Variant
Me![UnitPrice] = Me![ProductID].Column(2)
Me![ProductName] = Me![ProductID].Column(1)
Me![GLAcct] = Me![ProductID].Column(3)
Me.Dirty=False
'pos = Me.Bookmark
'Me.Requery
'Me.Bookmark = pos
End Sub
To requery a form and return to the same record, do this:
Dim lngPKValue As Long
With Me.RecordsetClone
lngPKValue = Me!ID
' Me.Dirty = False is unnecessary, as the Requery saves the data
Me.Requery
.FindFirst "[ID]=" & lngPKValue
If Not .NoMatch Then
Me.Bookmark = .Bookmark
End If
End With
Now, where you put this code depents. I don't quite see in the original code why there's a need to requery, as the updates have been done to the record you're already on, so you're not really accomplishing anything by requerying and returning to the record you started on. But there are circumstances in which you'd want to do that (e.g., in an ordered recordset where you've edited values that will change the place of this particular record in the sorted result), and the above is the way to do it.
If it's necessary, you'd probably want to turn off painting of the form (Me.Painting = False, the Me.Painting = True once you've set the bookmark) or of the application (Application.Echo = False/True) so the screen doesn't flicker and you don't see the requerying and navigating. But be sure you add an error handler, since if an error happens while screen painting is turned off, your user may end up stuck and unable to continue.