I have a combo box called status with open and complete. I also have another combo box called approvals with options pending approved or rejected.
My goal is to make the option complete in status only available if someone chooses approved in the approval combo box first. I am not sure how to go about doing this.
I made a form to mimic your form as described and cam up with the following:
Option Compare Database
Option Explicit
Private Sub Status_AfterUpdate()
If Me.Status = "Complete" Then
If Nz(Me.Approvals, "") <> "Approved" Then
Me.Status = "Open"
End If
End If
End Sub
Private Sub Approvals_AfterUpdate()
If Me.Approvals <> "Approved" Then
If Me.Status = "Complete" Then
Me.Status = "Open"
End If
End If
End Sub
This solution forces the value back to Open when anything does not match your logic. You may want to throw in a message box to so the user understands why the change is forced.
Related
I apologize if this is too vague a question, but we're having the following problem:
We use a search form to find a record, then load it in a bound-control form where changes are made. Then we return to the search form and open another record. When we do that, the form's BeforeUpdate property fires a 3020 error, "Update without Add New or Edit" and stepping through the code it's referring to the FIRST opened record. This is strange because there is no explicit update call, but after much trial and error I think the error is thus:
Record #1 is opened via the form and changes are made. Record #2 is opened on that same form without closing the first recordset. Even though we now re-opened the form with the second record, Access still assumes we're editing record 1, i.e. that we're trying to edit 2 records concurrently. Same as though we had a datasheet form and we edited one row and then tried to edit a second row without saving the first. What I want to be able to do is have it automatically do an update on the first record when the form deactivates so loading a new record doesn't cause this conflict.
So the bottom line is this: **Is there a way, on say the form's Dirty or Deactivate event, that we can force the form's recordset to update and close ** before loading a second record?
I hope I made this clear enough, it's a complex problem, so any small guidance would help. Btw, you may ask, "Why are you running the same code to open the same form twice?" Good question! Hey it's an old and badly written app (the thing has GoSubs in it for Pete's sake) but I have no choice but to make the best of a bad situation.
EDIT: I was asked to post code, which is reasonable, but it's in several different places. So I have the data form, it has a "Search" button to go back to the search form for opening another record. The search button on the data entry form is:
Private Sub CommandSearch_Click()
On Error GoTo Err_CommandSearch_Click
DoCmd.OpenForm "Reference Form", acNormal 'This is the form that does the searching
Exit_CommandSearch_Click:
Exit Sub
Err_CommandSearch_Click:
MsgBox Err.Description
Resume Exit_CommandSearch_Click
End Sub
When a record is selected on that search form, then the new form is opened with this code. Now this is where it gets tricky. I'm not the original programmer, as I said I think it was written in Access 97 by someone after taking an hour to read "Access for Dummies" :). But it always looks like only one copy of the form is open, so maybe it's re-opening it?
Public Sub CommandLoadCase_Click()
Dim LoadUTUCaseNumber As String, lengthUTUCaseNumber As Integer
lengthUTUCaseNumber = InStr(Forms![Reference Form]![Reference Query SubForm]![UTU Case Number], "-") - 1
If (lengthUTUCaseNumber = 0) Then
LoadUTUCaseNumber = ""
Else
LoadUTUCaseNumber = Left$(Forms![Reference Form]![Reference Query SubForm]![UTU Case Number], lengthUTUCaseNumber)
End If
On Error Resume Next
Forms![Case Appeal_Add-On Form].UTUCaseKeyToFind = LoadUTUCaseNumber
DoCmd.OpenForm "Case Appeal_Add-On Form", acNormal, , , , , LoadUTUCaseNumber
'Case Appeal Add On Form referred to here is the data entry form I refer to above.
End Sub
Finally, the Error 3020 (Update without Add/Edit) is occurring after it executes this line on the data entry form. (I know the code is complicated which is why I didn't enter it at first).
Private Sub Form_BeforeUpdate(Cancel As Integer)
[UTU Claim Sequence Key] = [UTU Claim Alpha Sequence] & CStr([UTU Claim Numeric Sequence])
End Sub
I have a form in Access 2007, which has an "update" routine, that enables or disables certain textboxes based on values in other fields (textboxes, checkboxes, comboboxes). The regular operation of that routine works well.
Now I found that pressing ESC calls the undo function, that restores the original values in all fields. But this undo does not call the events on those fields, so the form is in a wrong state, where textboxes are disabled/enabled although they shouldn't.
I also found that there is an undo-event, but that is useless for me because it is called before undo. I need an event after undo. What can I do here to update the fields when ESC is pressed?
I like this solution more, because it works not only on the "ESC"-Key:
private Sub form_Undo(cancel as integer)
afterUndo = true
TimerInterval = 1
end Sub
private Sub Form_Timer()
if afterUndo then
'do something after the Undo-Event
end if
TimerInterval = 0
end Sub
Well, like many times before I have an idea for a solution after postion the question.
The solution here is enabling KeyPreview on the form and using the KeyUp event. The undo is called on KeyDown, so when KeyUp is raised, the form already has the restored values again and the update routine works.
Another solution to this problem is to use each control's OldValue property. Through experimentation, I've found that the three different value properties of controls come into play in different situations:
Control.Value or simply Control
The control's current value most of the time
When the control has focus, it is the value the control had before gaining focus
During a Form.Undo event, it is the value the control prior to the Undo
Relevant during the Control.AfterUpdate and Control.Undo events
Control.Text
The control's value while it has focus
Relevant during events that occur as the user types, such as Control.Change, Control.KeyUp, Control.KeyDown
Control.OldValue
The value the control had when the current record was first opened
Also the value that a form-level Undo will reset the control to
Relevant during the Form.Undo event
The timer-based answer to this question is my go-to solution, but if you're already handling events as the user types (e.g., for live validation), then code like this can be sensible:
Private Sub LastName_Change()
ValidateLastName SourceProperty:="Text"
End Sub
Private Sub LastName_Undo(Cancel As Integer)
ValidateLastName SourceProperty:="Value"
End Sub
Private Sub Form_Undo(Cancel As Integer)
ValidateLastName SourceProperty:="OldValue"
End Sub
Private Sub ValidateLastName(SourceProperty As Variant)
Dim LastName As String
Select Case SourceProperty
Case "LastName"
LastName = Nz(Me.LastName.Text, "")
Case "Value"
LastName = Nz(Me.LastName.Value, "")
Case "OldValue"
LastName = Nz(Me.LastName.OldValue, "")
Case Else
Debug.Print "Invalid case in ValidateLastName"
Exit Sub
End Select
' <Do something to validate LastName>
End Sub
Note that this method does not get you access to the post-Form-Undo value of Control.Column(x) of combo/list boxes. You can get it by using DLOOKUP on Control.OldValue, but you're probably better off just using the timer-based solution instead.
New Edition June 2, 2017
I'm creating a database for a preventative maintenance program for the equipment in my facility. We have about 70 different machines with individuals preventative maintenance instructions, and I've been able to create tables for machines with the instructions attached to them by using forms that will update the database. I created a main form that will filter which PM needs to be done based on the date that is searched that will filter through the machines in the database, access does this fine. The problem I'm currently having trouble with is finding a way to add checkboxes to this main form, and print off only the ones that are selected with a button. The plan we have is to have the mechanics to be able to print off the PM instructions, and use the print off to update the database. Is there a way this can be done with checkboxes or optionboxes?Here's a picture of the main form that doesn't have the checkbox field attached to it yetHere's a picture of the forms that will show the preventative maintenance instructions that we will be printed.
Your question is a little vague and doesn't provide any insight on what you might have tried (this would actually help us determine what you want to do). That said, based on what's given:
If you have a single checkbox and want some action to be performed when it is checked/unchecked.
Private Sub CheckboxName_Click()
If CheckboxName.Value = -1 Then
'Do something
Else
'Do something else (Else is optional)
End If
End Sub
If you have multiple checkboxes you want to confirm which are checked and which aren't when a button or something is pushed you use the Controls object to cycle through all the checkboxes.
Private Sub ButtonName_Click()
Dim ctrl as Control
Dim i as Integer
For Each ctrl In Me.Controls
If ctrl.ControlType = acCheckBox and ctrl.Value = -1
'Do something (e.g., Select Case ctrl.name)
End If
End Sub
Hopefully that at least points you in the right direction.
I have an unbound form used to enter constituent data. I can't use subforms because data is spread across multiple tables and linked with foreign keys. Since I want to use the field validation rules to validate, I use a transaction to make sure new records are committed in an all-or-nothing fashion. I don't want names committed without addresses, etc.
The form is designed to iterate over a recordset, "tempImportRs", to give the user an opportunity to make sure the data looks OK and to manually correct or skip invalid entries. It works by populating the form's fields with values from tempImportRs for each entry. There's also a subform that searches for similar records and displays them to the user to decide if this is really a new person or if they already exist in the DB with somewhat different info.
I am running into a problem using this "tempImportRs" recordset, which persists for the lifetime of the form and is used by various methods in the form, alongside Access's transactions (which I don't understand very well, admittedly). If at any point I close the transaction like
WrkSp.Close
then my tempImportRs object will disappear and give me "Requires object" type errors. If I don't close the transaction, which is bad practice anyway, my form will work like I want for up to maybe a dozen records before giving me the error "Could not start transaction; too many transactions already nested."
How can I close the transaction cleanly, without destroying "tempImportRs"?
Here is a very simplified version of the VBA code for my form:
Public tempImportRs As RecordSet
Sub TryToAddRecords()
'Try to add the data from the now-populated form fields to my tables'
On Error GoTo Error_TryToAddRecords
Dim WrkSp As Workspace
Set WrkSp = DBEngine.Workspaces(0)
WrkSp.BeginTrans
'In my real code I use several recordsets to store the imported data'
'but I am simplifying here by using just one:'
Dim someOtherRs As Recordset
Set someOtherRs = CurrentDb.OpenRecordset("tblNamesAddressesEtc")
'...Do stuff using several other recordsets and '
'the form fields populated from tempImportRs'
someOtherRs.Update
WrkSp.CommitTrans
Exit_TryToAddRecords:
someOtherRs.Close
'I leave tempImportRs open for now.'
'WrkSp.Close <--This is what messes up tempImportRs'
Set someOtherRs = Nothing
Set WrkSp = Nothing
Exit Sub
Error_TryToAddRecords:
MsgBox Err.Description & vbNewLine & _
"Please fix the field and try again."
Resume Exit_TryToAddRecords
End Sub
Private Sub GoButton_click
Set tempImportRs = CurrentDb.OpenRecordset("tblTempImport")
If !tempImportRs.EOF
'<populate the form fields with the current '
'tempImportRs data here>'
TryToAddRecords
tempImportRs.MoveNext
Else
tempImportRs.close
Set tempImportRs = Nothing
MsgBox "All records imported"
End If
End Sub
This is not a problem specific to transactions and workspaces, but a more general matter of program structure. You should just make the workspace persist for the duration of the form, and commit to it multiple times. You don't need to make a new workspace for each transaction.
So, treat the workspace just like tempImportRs is treated in the code example. You can both set and close WrkSp in the GoButton_click sub. Don't set or close it in the TryToAddRecords function.
You will also have to make sure to roll back with WrkSp.Rollback if there's an error, add that line to Error_TryToAddRecords.
Public tempImportRs As RecordSet
Dim WrkSp As Workspace
'...
Private Sub GoButton_click
Set tempImportRs = CurrentDb.OpenRecordset("tblTempImport")
Set WrkSp = DBEngine.Workspaces(0) 'move this here
If !tempImportRs.EOF
'<populate the form fields with the current '
'tempImportRs data here>'
TryToAddRecords
tempImportRs.MoveNext
Else
tempImportRs.Close
Set tempImportRs = Nothing
'close the workspace here when you're done with tempImportRs
WrkSp.Close
Set WrkSp = Nothing
MsgBox "All records imported"
End If
End Sub
PS: Sorry folks for asking and answering this dumb question, I had a mental block and thought the issue was about managing workspaces and not a simpler matter of code design. Maybe this will still help someone someday.
I'm currently working on a Database which requires the following functionality:
For example given a specific project, I have a series of structures which belong to that project, which are displayed in a datasheet view on the project form. I am attempting to allow the user to on double click to navigate to that specific structure which is displayed on another form. Currently I am using filters to implement this behavior, however, this results in the filter being left on, and when I manually turn off the filter, the form I switch to returns back to the first entry.
I am using the current code on the datasheet:
Private Sub struct_name_DblClick(Cancel As Integer)
LookupValue = Me.struct_ID
Form_frm_control.pg_structure.SetFocus
Form_frm_control.subform_structure.Form.Filter = "struct_ID = " & LookupValue
Form_frm_control.subform_structure.Form.FilterOn = True
End Sub
Any help would be greatly appreciated. Thanks in advance.
It all depends on what you need to do.
If you want to display all records and navigate to the selected record, then you can use bookmark navigation:
With Forms!MyOtherForm
.RecordsetClone.FindFirst "struct_ID = " & Me!struct_ID
If Not .RecordsetClone.NoMatch Then
If .Dirty Then
.Dirty = False
End If
.Bookmark = .RecordsetClone.Bookmark
End If
End With
This assumes that the other form is open with all the records loaded.
Another approach to this problem, which I find more useful for popup situations like this, is to just open the other form as a dialog and require it be closed before returning to the calling context. In that case, you'd do this:
DoCmd.OpenForm "MyOtherForm", , , "struct_ID = " & Me!struct_ID, , acDialog
You'd then have to close the other form to get back to the original context.
You might think that with large numbers of records this would be inefficient, but it's actually very fast, as this operation is highly optimized within Access (moreso than filtering an already open form, in fact).