Here's some background - i'm creating an access form that allows users in a call centre to select options from several combo and tickboxes and it generates the relevant call script into a textbox based on their selections.
I'm in the process of writing the VBA code that adds together various paragraphs of pre-determined text into a textbox based on the options selected. I've created a string for each of these paragraphs of text in VBA and i'm trying to write the IF statements to put them together, however there will eventually be around 50 different option variations and i'm struggling to find a way around this without writing 50 different If/ElseIf statements :|
Below is a snippet of what I've come up with so far, with the only difference in the ElseIf being that [FullRefund] is False which replaces the 'FullRef' string with 'OPLim' in the textbox value
If [PreAuthType] = "Out Patient" _
And [FullRefund] = True _
And [ExcessLimit] > 0 _
And [Text58] < 93 _
And [OpenRef] = False _
And [TxtReq] = False _
And [ClaimsBonus] = True Then
Me.GenScript.Value = Stan & FullRef & Excess & Renewal & OpenRef & NoText & BonusTxt
ElseIf [PreAuthType] = "Out Patient" _
And [FullRefund] = False _
And [ExcessLimit] > 0 _
And [Text58] < 93 _
And [OpenRef] = False _
And [TxtReq] = False _
And [ClaimsBonus] = True Then
Me.GenScript.Value = Stan & OPLim & Excess & Renewal & OpenRef & NoText & BonusTxt
End If
Any help or guidance you can offer to at least get me going would be amazing :)
Basically, instead of writing a separate IF statement for every possible result, build the result step by step. The very purpose of computer programs is to eliminate repetition, not to increase it. There are a number of ways you could tackle this, but here's an example of the most basic because this is (believe it or not) an extremely simple set of logic comparisons.
This is already the majority of the coding that will be required...
Sub BuildScript()
Dim scrOut As String 'a string to use to build the output
scrOut = stan 'standard beginning for all results, I assume?
If fullrefund Then 'if [full refund] is true then...
scrOut = scrOut & FullRef '...add this top the end of [scrOut]...
Else
scrOut = scrOut & opLim '...otherwise add this to [scrOut].
End If
If ExcessLimit > 0 Then
scrOut = scrOut & excess
Else
scrOut = scrOut & "___something else if excessLimit <= 0___"
End If
If Text58 < 93 Then
scrOut = scrOut & renewal
Else
scrOut = scrOut & "___something else if Text58 >= 93___"
End If
If openRef Then
scrOut = scrOut & openRef
Else
scrOut = scrOut & "___something of openRef=false"
End If
If TxtReq Then
scrOut = scrOut & " ___something if txtReq=true___"
Else
scrOut = scrOut & NoText
End If
If ClaimBonus Then
scrOut = scrOut & BonusTxt
Else
scrOut = scrOut & "___something else if ClaimBonus=False___"
End If
MsgBox scrOut 'display a message box with the result
'(don't worry about what to do with it until later)
End Sub
Edit:
Forgetting for a minute about the above code, I'll demonstrate your 3-option example:
PreAuthType (4 values), Excess (Number), and OpenRef (true/false). if option A, B, or C of PreAuthType is selected then the 'Stan' and 'Excess' strings need to be returned, however if ExcessLimit is >0 then 'Excess' needs to also be included, and if OpenRef is True then 'OpenReferral' also needs to be included.
PreAuthType: one of: A, B, C, D
Excess: numeric value from ___ to ___
OpenRef: TRUE or FALSE only
Select Case PreAuthType
Case A, B, C
scrOut = Stan & Excess
Case D
'(does this option get anything specific?)
End Select
If Excess > 0 then
scrOut = scrOut & "The Excess is $" & Excess
end if
If OpenRef the
scrOut = scrOut & "Referral " & OpenReferral & " needs to be blah blah."
End If
Note that in the IF statements above:
If OpenRef then...
is exactly the same as writing:
If OpenRef = True then...
Similarly, this:
If Not OpenRef then...
is identical to this:
If OpenRef = False then...
More Information:
Using If...Then...Else Statements
(look at a criteria and choose between 2 results.)
VBA for Beginners: VBA Case Function in a Nutshell ("Select..Case")
(look at a criteria and choose between many results)
VBA 'Like' Operator
(compare 'close matches', ie: if it starts with A then do this)
Related
I have a subform, and it has a field with code source (custom VBA function doing a lookup). The filter button on that field doesn't work (Access by design). My users will want to use filters for this field and also for other fields.
As a workaround, I have added 3 checkboxes. If a user clicks any one of these checkboxes, filters get applied to the subform based on the function field.
The problem is, this removes all the other currently applied filters from the subform. This is not nice towards my users.
Is there a way to add and remove one field criteria to filtering without ruining the rest of the filter?
I have tried brute forcing it, but I gave up. When a filter gets added the normal way, many parentheses and AND words get added. My little filter text can be anywhere in a maze of filter criteria string. So using text functions to find it and manipulate it seems to be big, slow, stupid, unstable and dirty.
Am I missing something here? Any better way to do this?
Dim tx As String
If Not Me.flProcDONE And Not Me.flProcNOK And Not Me.flProcOK Then
tx = ""
Else
tx = "stProc IN (" & IIf(Me.flProcDONE, kStProcUPD, "99") _
& "," & IIf(Me.flProcNOK, kStProcNOK, "99") _
& "," & IIf(Me.flProcOK, kStProcOK, "99") & ")"
End If
With Me.sfrApply.Form
.Filter = tx
.FilterOn = True
End With
(Partly-working) brute force code:
With Me.sfrApply.Form
If .Filter = "" Then
.Filter = tx
Else
If tx = "" Then
lnStart = InStr(1, .Filter, "AND stProc IN (", vbTextCompare)
If lnStart > 0 Then
lnEnd = InStr(lnStart, .Filter, ")", vbTextCompare)
.Filter = Left(.Filter, lnStart - 1) & Mid(.Filter, lnEnd + 1)
End If
Else
lnStart = InStr(1, .Filter, "stProc", vbTextCompare)
If lnStart > 0 Then
lnEnd = InStr(lnStart, .Filter, ")", vbTextCompare)
.Filter = Left(.Filter, lnStart - 1) & tx & Mid(.Filter, lnEnd + 1)
Else
.Filter = "(" & .Filter & ") AND (" & tx & ")"
End If
End If
End If
.FilterOn = True
End With
It has a few errors, misses some parentheses. Making it work would require an additional 4-5 IFs and many more Instrs. Disgusting. Access filtering keeps adding [] and () to the filter text, that is what makes it near impossible to manipulate from the code.
A few examples of .Form.Filter texts:
"" - no filter
"stProc IN (99,99,1)" - the one I'm trying to manipulate
"([scrCarrierInvoiceGLSQuote].[ctParcelQUOTE] In (1,2))"
"((([stProc] In (99,99,1)))) AND ([scrCarrierInvoiceGLSQuote].[ctParcelSI]=1)"
"(([scrCarrierInvoiceGLSQuote].[ctParcelQUOTE] In (1,2))) AND (stProc IN (99,99,1))"
"((([scrCarrierInvoiceGLSQuote].[ctParcelQUOTE] In (1,2)) AND ([stProc] In (99,99,1)))) AND ([scrCarrierInvoiceGLSQuote].[lsError] Like "COD?")"
I would try something like
With Me.sfrApply.Form
.Filter = .Filter & " AND " & tx
.FilterOn = True
End With
This is just a quick sample but you can elaborate on that.
Well, I did manage to solve it. There was no nice way I have found. Basically:
If the filter string is empty, just add my filter string to it
If the filter part in question is not already in the filter string, just Concatenate it to the end of it (as #iDevlop suggested)
If the filter I'm about to apply is already part of the filter, just change the "IN(...)" part of it - never attempt to remove it.
Here is the code:
Dim txFullFilter As String
Dim txFilterPart As String
Dim lnStProcPos As Long 'Position of the column name in the existing filter text
Dim lnOpenParPos As Long 'Position of the opening parentheses "(" after column name
Dim lnCloseParPos As Long 'Position of the closing parentheses ")" after the opening one
'Create the actual filter text form the column I'm trying to filter from outside.
If Not Me.flProcDONE And Not Me.flProcNOK And Not Me.flProcOK Then
txFilterPart = "0,1,3,7"
Else
txFilterPart = IIf(Me.flProcDONE, kStProcUPD, "99") _
& "," & IIf(Me.flProcNOK, kStProcNOK, "99") _
& "," & IIf(Me.flProcOK, kStProcOK, "99")
End If
txFullFilter = "stProc IN (" & txFilterPart & ")"
'Apply said filter to the subform
With Me.sfrApply.Form
If .Filter = "" Then
.Filter = txFullFilter
ElseIf InStr(.Filter, "stProc") > 0 Then
lnStProcPos = InStr(.Filter, "stProc")
lnOpenParPos = InStr(lnStProcPos, .Filter, "(")
lnCloseParPos = InStr(lnOpenParPos, .Filter, ")")
.Filter = Left(.Filter, lnOpenParPos) & txFilterPart & Mid(.Filter, lnCloseParPos)
Else
.Filter = .Filter & "AND " & txFullFilter
End If
.FilterOn = True
End With
I have the code below (courtesy of allenbrowne, his info are in the code). When I am filtering using one of the columns (One_or_Two_Pearl) it is giving me a (Syntax error missing operator in query expression). I can not trace the problem as everything look fine. That column is defined as Text type and contains data (1, 2, NA). This problem is only occurring for this column, and when I debug the yellow mark is indicating the (Me.Filter = strWhere) part.
Thank you in advance for your help.
'http://allenbrowne.com/ser-62.html
'Purpose: This module illustrates how to create a search form, _
where the user can enter as many or few criteria as they wish, _
and results are shown one per line.
'Note: Only records matching ALL of the criteria are returned.
'Author: Allen Browne (allen#allenbrowne.com), June 2006.
Option Compare Database
Option Explicit
Private Sub cmdFilter_Click()
'Purpose: Build up the criteria string form the non-blank search boxes, and apply to the form's Filter.
'Notes: 1. We tack " AND " on the end of each condition so you can easily add more search boxes; _
we remove the trailing " AND " at the end.
' 2. The date range works like this: _
Both dates = only dates between (both inclusive. _
Start date only = all dates from this one onwards; _
End date only = all dates up to (and including this one).
Dim strWhere As String 'The criteria string.
Dim lngLen As Long 'Length of the criteria string to append to.
Const conJetDate = "\#mm\/dd\/yyyy\#" 'The format expected for dates in a JET query string.
'***********************************************************************
'Look at each search box, and build up the criteria string from the non-blank ones.
'***********************************************************************
'Text field example. Use quotes around the value in the string.
If Not IsNull(Me.cboxprojphase) Then
strWhere = strWhere & "([Project_Phase] = """ & Me.cboxprojphase & """) AND "
End If
'Another text field example. Use Like to find anywhere in the field.
If Not IsNull(Me.cboxcontract) Then
strWhere = strWhere & "([Contract] = """ & Me.cboxcontract & """) AND "
End If
'Number field example. Do not add the extra quotes.
If Not IsNull(Me.cboxdesigndpm) Then
strWhere = strWhere & "([Design_DPM] = """ & Me.cboxdesigndpm & """) AND "
End If
'Text field example. Use quotes around the value in the string.
If Not IsNull(Me.cboxadmupc) Then
strWhere = strWhere & "([ADM/UPC] = """ & Me.cboxadmupc & """) AND "
End If
'Text field example. Use quotes around the value in the string.
If Not IsNull(Me.cboxpearl) Then
strWhere = strWhere & "([One_or_Two_Pearl] = """ & Me.cboxpearl & """) "
End If
'***********************************************************************
'Chop off the trailing " AND ", and use the string as the form's Filter.
'***********************************************************************
'See if the string has more than 5 characters (a trailng " AND ") to remove.
lngLen = Len(strWhere) - 5
If lngLen <= 0 Then 'Nah: there was nothing in the string.
MsgBox "No criteria", vbInformation, "Nothing to do."
Else 'Yep: there is something there, so remove the " AND " at the end.
strWhere = Left$(strWhere, lngLen)
'For debugging, remove the leading quote on the next line. Prints to Immediate Window (Ctrl+G).
'Debug.Print strWhere
'Finally, apply the string as the form's Filter.
Me.Filter = strWhere
Me.FilterOn = True
End If
End Sub
Private Sub cmdReset_Click()
'Purpose: Clear all the search boxes in the Form Header, and show all records again.
Dim ctl As Control
'Clear all the controls in the Form Header section.
For Each ctl In Me.Section(acHeader).Controls
Select Case ctl.ControlType
Case acTextBox, acComboBox
ctl.Value = Null
Case acCheckBox
ctl.Value = False
End Select
Next
'Remove the form's filter.
Me.FilterOn = False
Me.OrderByOn = False
End Sub
Private Sub Form_BeforeInsert(Cancel As Integer)
'To avoid problems if the filter returns no records, we did not set its AllowAdditions to No.
'We prevent new records by cancelling the form's BeforeInsert event instead.
'The problems are explained at http://allenbrowne.com/bug-06.html
Cancel = True
MsgBox "You cannot add new Records to the search form.", vbInformation, "Permission denied."
End Sub
Private Sub Form_Open(Cancel As Integer)
'Remove the single quote from these lines if you want to initially show no records.
'Me.Filter = "(False)"
'Me.FilterOn = True
Dim strURL As String
Dim objIE As Object
Dim arrSites(2) As String
Dim i As Integer
arrSites(0) = "http://google.com"
arrSites(1) = "http://google.com"
Set objIE = CreateObject("InternetExplorer.Application")
For i = 0 To 1 Step 1
strURL = arrSites(i)
If i = 0 Then
objIE.Navigate strURL
Else
objIE.Navigate2 strURL, 2048
End If
Next i
objIE.Visible = True
Set objIE = Nothing
'objIE.Quit
End Sub
Private Sub optSortorder_AfterUpdate()
If Me.optSortorder = 1 Then
Me.OrderBy = Me.cboSortField
Else
Me.OrderBy = Me.cboSortField & " DESC"
End If
Me.OrderByOn = True
End Sub
Both your problems are here:
strWhere = Left$(strWhere, lngLen)
'For debugging, remove the leading quote on the next line. Prints to Immediate Window (Ctrl+G).
'Debug.Print strWhere
Uncomment the Debug.Print line to see what is happening:
it tries to remove a trailing " AND " from your [One_or_Two_Pearl] line, but there is none.
I'm working on combining two excel worksheets. Before I start, I'd like to mention that I also have mysql workbench, so I'm open to working on this issue in either sql or vba (I should learn both). I'm working with .bed files, which are lists of genomic coordinates. In short, the data is indexed by chromosome number (ie:chr2) and then has a numerical start and stop location on the chromosome. These numerical locations can span a large range (ie:100-10,000) or be a single position (ie: 999-1000). I have a list of coordinates that cover a large range, and in a separate file I have a list of single positions.
Example of a file with ranges:
chromosome start stop
chr1 4561 6321
chr3 9842 11253
Example of file with single positions:
chromosome start stop
chr1 5213 5214
chr3 10254 10255
I would like to combine these worksheets such that if a location in my list of single positions is found within the range in my list of ranges, the locations for both are listed in the same row. The lists are 1000s of locations long, so I'd also like this program to loop through every row. Using the example data listed above, I'd like my output to look like the following:
Example of desired output:
chromosome start stop chromosome start stop
chr1 4561 6321 chr1 5213 5214
chr3 9842 11253 chr3 10254 10255
There is a high probability that multiple single positions will fall within a single range, and I would like these to be listed as separate rows.
I appreciate any help I can get! Thank you in advance. I am eager to learn!
Here's a basic outline which queries two tables on sheets named "Ranges" and "Positions", and outputs the results on a sheet named"Results"
The input tables should have headers, and start in the top-left cell (A1)
Sub SqlJoin()
Dim oConn As New ADODB.Connection
Dim oRS As New ADODB.Recordset
Dim sPath
Dim sSQL As String, wb As Workbook
Set wb = ThisWorkbook
sSQL = " select a.chromosome, a.start, a stop," & _
" b.chromosome, b.start, b.stop " & _
" from <ranges_table> a, <positions_table> b" & _
" where b.start >= a.start and b.stop <= a.stop"
sSQL = Replace(sSQL, "<ranges_table>", _
Rangename(wb.Worksheets("Ranges").Range("A1").CurrentRegion))
sSQL = Replace(sSQL, "<positions_table>", _
Rangename(wb.Worksheets("Positions").Range("A1").CurrentRegion))
If wb.Path <> "" Then
sPath = wb.FullName
Else
MsgBox "The workbook must be saved first!"
Exit Sub
End If
oConn.Open "Provider=Microsoft.ACE.OLEDB.12.0;Data Source='" & sPath & "';" & _
"Extended Properties='Excel 12.0;HDR=Yes;IMEX=1';"
oRS.Open sSQL, oConn
If Not oRS.EOF Then
wb.Worksheets("Results").Range("A2").CopyFromRecordset oRS
Else
MsgBox "No records found"
End If
oRS.Close
oConn.Close
End Sub
Function Rangename(r As Range) As String
Rangename = "[" & r.Parent.Name & "$" & _
r.Address(False, False) & "]"
End Function
This is by no means essential, but I would like to find out how to create more efficient code, and i'm sure this is far from efficient!
On the form disabled fields values are cleared before the form is saved.
The below code send a message to the user to inform them that they may lose some data if they leave a checkbox unchecked.
In the context of the form it all makes sense, i would just like to know a simpler methodology, i'm sure i could use an array somewhere but cant quite figure it out.
Dim couldLoseData As Boolean
Dim msgStr As String
couldLoseData = False
If (Me.chkInvSent = False) And (Not IsNull(Me.invoicedDate)) Then
couldLoseData = True
msgStr = "Invoice Sent"
End If
If (Me.chkFeePaid = False) And (Not IsNull(Me.datePaid)) Then
couldLoseData = True
If msgStr = "" Then
msgStr = "Claim Fee Paid"
Else
msgStr = msgStr & " / Claim Fee Paid"
End If
End If
If (Me.chkFeeLodged = False) And (Not IsNull(Me.lodgedDate)) Then
couldLoseData = True
If msgStr = "" Then
msgStr = "Fee Lodged"
Else
msgStr = msgStr & " / Fee Lodged"
End If
End If
If couldLoseData = True Then
If MsgBox("You will lose data in the following areas as the relevant checkboxes are unticked." & vbNewLine & vbNewLine & _
msgStr & vbNewLine & vbNewLine & "Do you wish to continue?", vbYesNo, dbNameOf) = vbNo Then
Cancel = True
End If
Else
'
'
'
' Procedure that gets carried out here
End If
No biggie but if any one could offer me a simpler solution it would be appreciated.
Cheers
Noel
I'm not sure why you think you should be using arrays. When it comes to the msgStr variable logic I would just put in the following:
msgStr = msgStr & "Invoice Sent / "
rather than the five lines of If msgstr = "" Then, etc, etc, End If lines.
Then at the end I would put in the following line
msgStr = lef(msgStr, len(msgStr) - 3) ' remove the trailing /
This then removes the trailing " / "
Purists will tell you that you should never add anything to a string you later remove. I say, so long as you leave a comment there for the next person who is reading your code, this reduces complexity of your preceding lines of code making it much easier to grasp exactly what is going on.
Whenever I'm looking for a value to be returned from a MsgBox I place the string creating in a separate line of code. Thus is much easier to see, at a glance, exactly what the code is doing.
strMsg = "You will lose data in the following areas as the relevant checkboxes are unticked." & vbNewLine & vbNewLine & _
msgStr & vbNewLine & vbNewLine & "Do you wish to continue?"
If MsgBox(strMsg, vbYesNo, dbNameOf) <> vbYes Then _
Cancel = True
If I'm only setting one value in the If statement, such as you show, I will also put in the _ and thus not require the End If.
I also prefer <> vbYes just in case something wonky should happen or if someone, not you of course, mucks with the msgbox options.
Why do you even allow the user to close the form when all the data fields have not been filled out?
Basically, to me, your logic is all in the wrong place. If you have a CLOSE button on your form (assuming you've gotten rid of the default Windows CLOSE X), you would not enable it until such time as all the data fields have been filled out appropriately.
The way I usually do this is to write a subroutine (or function) that checks all the fields that have to be filled out and enables the CLOSE button if everything is in order. Thus, the user CAN'T close the form until all the appropriate fields are filled out, except, perhaps, if you've provided a CANCEL button (in which case, you WANT to lose the data).
You don't need arrays but a simple helper method to simplify code and make it more reusable:
(just replace checkboxes and conditions in the following code)
Public Function ErrorChecker(assumption As Boolean, errorMessage As String, condition As Boolean, concatenate As Boolean) As String
Dim ret As String = [String].Empty
If Not assumption AndAlso condition Then
If concatenate Then
ret += " / "
End If
ret += errorMessage
End If
Return ret
End Function
Private Sub button1_Click(sender As Object, e As EventArgs)
Dim message As String = [String].Empty
message += ErrorChecker(checkBox1.Checked, "Error 1", value1 Is Nothing, False)
message += ErrorChecker(checkBox2.Checked, "Error 2", value2 Is Nothing, True)
message += ErrorChecker(checkBox3.Checked, "Error 3", value3 Is Nothing, True)
If message <> String.Empty Then
'messagebox
End If
End Sub
I've written a simple function to concatenate two strings that eliminates the need to worry about whether you need to strip anything off when you're done concatenating. Here's the function:
'-----------------------------------------------------------------------------
' Purpose : Concatenates two strings
' Usage : Dim MyList As String
' MyList = Conc(MyList, SomeValue)
' Notes : Eliminates the need to strip off the leading/trailing delimiter
' when building a string list
'-----------------------------------------------------------------------------
Function Conc(StartText As String, NextVal, _
Optional Delimiter As String = ", ") As String
If Len(StartText) = 0 Then
Conc = Nz(NextVal)
ElseIf Len(CStr(Nz(NextVal))) = 0 Then
Conc = StartText
Else
Conc = StartText & Delimiter & NextVal
End If
End Function
And here's how I'd rewrite your code using this function:
Dim msgStr As String
If (Me.chkInvSent = False) And (Not IsNull(Me.invoicedDate)) Then
msgStr = Conc(msgStr, "Invoice Sent", " / ")
End If
If (Me.chkFeePaid = False) And (Not IsNull(Me.datePaid)) Then
msgStr = Conc(msgStr, "Claim Fee Paid", " / ")
End If
If (Me.chkFeeLodged = False) And (Not IsNull(Me.lodgedDate)) Then
msgStr = Conc(msgStr, "Fee Lodged", " / ")
End If
If Len(msgStr) > 0 Then
If MsgBox("You will lose data in the following areas as the relevant checkboxes are unticked." & vbNewLine & vbNewLine & _
msgStr & vbNewLine & vbNewLine & "Do you wish to continue?", vbYesNo, dbNameOf) <> vbYes Then
Cancel = True
End If
Else
' Procedure that gets carried out here
End If
This is how I'd code it up
Dim couldLoseData As Boolean
Dim msgStr As String
Dim InvBoolean as boolean
Dim PaidBoolean as boolean
Dim LodgedBoolean as boolean
Dim response as integer
couldLoseData = False
InvBoolean = (Me.chkInvSent = False) And (Not IsNull(Me.invoicedDate))
PaidBoolean = (Me.chkFeePaid = False) And (Not IsNull(Me.datePaid))
LodgedBoolean = (Me.chkFeeLodged = False) And (Not IsNull(Me.lodgedDate))
couldLoseData = InvBoolean or PaidBoolean or LodgeBoolean
'if any one is true, there could be lost data.
if couldLoseData = false then
exit sub 'bail if nothing applies
'you may want a GOTO if there is stuff this sub needs to do regardless
end if
If InvBoolean = true then 'add phrase and move to new line
msgStr = msgStr & "Invoice Sent" & vbcrlf
end if
If PaidBoolean = true then 'add phrase and move to new line
msgStr = msgStr & "Claim Fee Paid" & vbcrlf
end if
If LodgedBoolean = true then 'add phrase and move to new line
msgStr = msgStr & "Fee Lodged" & vbcrlf
end if
If couldLoseData = True Then
msgStr = "You will lose data in the following areas as the relevant checkboxes are unticked." & vbcrlf & msgStr & vbcrlf
msgStr = msgStr & "Do you wish to continue?"
response = msgbox(msgstr, vbYesNo)
if response = vbno then
Cancel = True
End If
end if
If you really were looking to use an array:
Dim couldLoseData As Boolean
Dim msgStr As String
Dim ConditionsResponses(0 to 2,1)
Dim x as integer
Dim response as integer
couldLoseData = False
ConditionsResponses(0,0) = (Me.chkInvSent = False) And (Not IsNull(Me.invoicedDate))
ConditionsResponses(1,0) = (Me.chkFeePaid = False) And (Not IsNull(Me.datePaid))
ConditionsResponses(2,0) = (Me.chkFeeLodged = False) And (Not IsNull(Me.lodgedDate))
ConditionsResponses(0,1) = "Invoice Sent" & vbcrlf
ConditionsResponses(1,1) = "Claim Fee Paid" & vbcrlf
ConditionsResponses(2,1) = "Fee Lodged" & vbcrlf
couldLoseData = ConditionsResponses(0,0) or ConditionsResponses(0,0) or ConditionsResponses(0,0)
'if any one is true, there could be lost data.
for x = 0 to 2
if ConditionsResponses(x,0)= true then
msgStr = msgStr & ConditionsResponses(x,1)
end if
next x
If couldLoseData = True Then
msgStr = "You will lose data in the following areas as the relevant checkboxes are unticked." & vbcrlf & msgStr & vbcrlf
msgStr = msgStr & "Do you wish to continue?"
response = msgbox(msgstr, vbYesNo)
if response = vbno then
Cancel = True
End If
end if
Well I am almost done finalizing the auditing portion of my application that I discussed here. The way I am doing it is looping through all text fields, drop down boxes and checkboxes and storing their values in the form_load event. Then I am doing the same thing in the form_afterUpdate event and comparing the two. If there is a difference I am logging it, if not I move on. Here is the code:
Dim strValues(1 To 32) As String
Private Sub Form_AfterUpdate()
Dim strCurrentValue, strSQL As String
Dim intCurrentField As Integer
intCurrentField = 1
For Each C In Forms!frmVendorsManageVendors.Controls
Select Case C.ControlType
Case acTextBox, acComboBox, acCheckBox
//Doing this because I don't want a NULL as it won't concatenate in the SQL query and don't want 0 or -1 for the boolean fields
strCurrentValue = IIf(IsNull(C), "", IIf(C = vbTrue Or C = vbFalse, IIf(C = vbTrue, "Yes", "No"), C))
If strValues(intCurrentField) <> strCurrentValue Then
strSQL = "INSERT INTO changesTable (change_time,user_affected,field_affected,old_value,new_value) VALUES (NOW()," & [id] & ",'" & C.ControlSource & "','" & strValues(intCurrentField) & "','" & strCurrentValue & "')"
DoCmd.SetWarnings False
DoCmd.RunSQL strSQL
//InputBox "", "", strSQL
strSQL = "WEEEE"
DoCmd.SetWarnings True
strValues(intCurrentField) = strCurrentValue
End If
intCurrentField = intCurrentField + 1
End Select
Next
End Sub
Private Sub Form_Open(Cancel As Integer)
Call btnLock_Click
Dim intCurrentField As Integer
intCurrentField = 1
For Each C In Forms!frmVendorsManageVendors.Controls
Select Case C.ControlType
Case acTextBox, acComboBox, acCheckBox
//Doing this because I don't want a NULL as it won't concatenate in the SQL query and don't want 0 or -1 for the boolean fields
strValues(intCurrentField) = IIf(IsNull(C), "", IIf(C = vbTrue Or C = vbFalse, IIf(C = vbTrue, "Yes", "No"), C))
intCurrentField = intCurrentField + 1
End Select
Next
End Sub
As you can see there is a commented out line where I insert into the changesTable that will put up the query in an input box so I can copy/paste it and look at it. When I uncomment that line everything is fine. If it is commented it generates the first change fine, but then won't change it for the other controls. So if I change field1 and field2 it will insert the field 1 change twice.
It is quite confusing and I have NO CLUE as to why this is happening.
Also I know I am using the wrong comment syntax but if I use the correct syntax the SO "code color"er doesn't display properly.
I'm not sure I have the whole answer, but a couple of observations.
You can eliminate some lines of code by using CurrentDB.Execute strSQL. This elminates the need for the SetWarnings calls. It executes directly against the database without interacting with the usual interface mechanisms.
For debugging purposes, it might be better to use Debug.Print to put your SQL string out to the Debug window. It avoids involving the user interface still puts the SQL where you can copy it to the clipboard if you want to grab it and work with it.
I think there's a slim chance that the DoCmd method call to execute your SQL, even with the calls to SetWarnnigs, might be tipping something in the interface to pull focus off of the form, like shahkalpesh suggested. I've done things like this and not seen the problem you are having, so my only advice on the problem itself is to do as I do and switch to CurrentDB.Execute and eliminate calls to DoCmd inside the loop.
Just curious -- why did you use an array for the previous values rather than using the OldValue property on the controls?
I am guessing that AfterUpdate might not be the right event to use.
Also, putting inputbox might be causing the existing control to loose focus (which is making it behave correctly).
I would suggest checking that each of your controls are being run over by putting a msgbox C.name inside the loop after select case.
Have you tried doing it with an execute statement (something like this)?
Dim db As DAO.Database 'Inside the transaction.
Set db = CurrentDB
strSQL = "INSERT INTO changesTable (change_time, user_affected, " & _
"field_affected, old_value, new_value) VALUES (NOW()," & [id] & _
",'" & C.ControlSource & "','" & strValues(intCurrentField) & _
"','" & strCurrentValue & "')"
db.Execute strSql