Adding logic to a sub form in Access? - ms-access

I have a sub form in Access:
The CopyNo is a combobox that lets me select from the MovieCopies table. When I select one, I want the Title field to show the correct movie title associated to that copy's movie ID. I also want the format to show. When I select from DaysRented combobox, if I select 1 and the movie is New, I want it to display the price, if it is regular for 3 days display the correct price etc.
I'm just not sure how to give logic to the comboboxes.
If anyone could point me in the right direction of how to do this sort of thing in Access 2007 I'd really appreciate it.
Thanks

Something like this:
Private Sub cboCopyNo_AfterUpdate()
If Nz(Me.cboCopyNo, "") <> "" Then
Me.txtTitle = DLookup("Title", "MovieMaster", "MovieID = " & Me.cboCopyNo)
End If
End Sub
Private Sub cboDaysRented_AfterUpdate()
If Nz(Me.cboDaysRented, 0) > 0 Then
Dim strType as String
strType = DLookup("[Type]", "MovieMaster", "MovieID = " & Me.cboCopyNo)
If Me.cboDaysRented = 1 Then
Me.txtPrice = DLookup("Price1Day", "Price", "[Type] = '" & strType & "'")
Else
Me.txtPrice = DLookup("Price3Day", "Price", "[Type] = '" & strType & "'")
End If
End If
End Sub
Couple notes. Some of your field names are reserved words in certain databases, such as "Type". I highly recommend you try to use field names that are not reserved words in Access or SQL server.
DLookups are not necessarily the fastest way to lookup data but will likely be fast enough for what you're trying to do here. Sometimes I create my own DAO recordset and lookup the values I want rather than using DLookup. It's basically like writing your own DLookup function.
DLookup uses SQL language so your syntax in the third argument, the WHERE clause, needs to match SQL. If the field in your WHERE clause is text/string you'll need to use a single quote on either side of the value (as shown above around the strType variable). If it is a number field you will not need the quotes. If it's a date you'll need hash signs (#).

Related

Editing listbox item

So i have a listbox that displays all the orders entered in the Order table using a select sql query. Now i also want to add the ability to edit the items from the listbox, i see the right click edit list item option but when i click it, it just opens the form but doesnt populate the fields. The form has textboxes that are unbound but i cant figure out how to get them to populate based on the right clicked entry. I have also tried to open the target form from vba and fill the fields through vba with the following code
Private Sub editrecordbttn_Click()
Dim valSelect As Variant
Dim v As Variant
Dim selector As Variant
Dim strValue As String ' just used for the demonstration
Dim splitvalue() As String
Dim selectedsampid As String
Dim selectedcusid As String
Dim Records As DAO.Recordset
Dim SQLcus As String
Dim SQLsamp As String
For Each valSelect In Me.searchlistbox.ItemsSelected
strValue = strValue & "" & Me.searchlistbox.ItemData(valSelect) & "," & "" & Me.searchlistbox.Column(1, valSelect) & ","
Next valSelect
' to remove trailing comma
strValue = Left(strValue, Len(strValue) - 1)
splitvalue() = Split(strValue, ",")
selectedsampid = splitvalue(0)
selectedcusid = splitvalue(1)
DoCmd.OpenForm ("Add Sample")
Forms![Add Sample].fnametxt.SetFocus
'query and fill cus info
SQLcus = "SELECT * FROM CustomerInfo WHERE CusID = '" & selectedcusid & "';"
Set Records = CurrentDb.OpenRecordset(SQLcus)
Me!clienttypetxt = Records![Client type].Value
End Sub
Ok, so say we have a listbox, and we do this:
The first column of the listbox is assumed to be the PK or "ID" of the rows.
so, we have this:
And thus you select a row, and then click on the button.
The button code would look like this:
Private Sub cmdEdit_Click()
Debug.Print "Hotel list id selected = " & Me.HotelList
DoCmd.OpenForm "frmEditHotels", , , "ID = " & Me.HotelList
End Sub
So, in most cases, for a better user experience, it probably better to approach things as per above.
There is of course the case in which you fill the listbox (or combo) with a "list" of values NOT from the database. In that case, you can use the "edit" list option. And this allows you to specify a form (or use the built in editor).
so, if this is NOT a list that you type in, and is from the database, then don't try to use the built in "list editing"
(add a button like above, and launch the form with the "where" clause to load the form to the ONE data record as I did above.
Since oh so very often, a listbox data will come from a table, then the edit list options are not really much particular use. And using a table (as opposed to a list) to fill + drive the combo/listbox is a much better design, and idea anyway.
This is especially the case if you ever want multiple-users, since the "list" edit feature would mean and suggest that each user editing the list would now have their own lists as opposed to using a table which everyone can edit.
Also, there is NO reason to use a loop to fill that list box. We can do this:
' setup critera for listbox.
Dim strSQL As String
' prompt user for Hotel city - we just hard code for this exmaple.
Dim strCity As String
strCity = "Banff"
strSQL = "SELECT ID,FirstName, LastName, City,HotelName FROM tblHotels " & _
"WHERE City = '" & strCity & "' " & "ORDER BY HotelName"
Me.HotelList.RowSource = strSQL
Note how we do not have some MESSAY value list, but can shove the data (sql) right to the listbox. Not only do we don't have loops, but we also don't have to worry about the size limits.
With "value list" (those messy delimited ";" list), then you have a rather small limit of 4,000 characters. Don't take many larger rows to "blow up" the listbox, since it can't handel very many rows.
In fact, I often still suggest you use the wizard to build the listbox, and you can choose a datasource (sql), or the MUCH lessor choice of "value list".
Value list is only a good choice if you have say a few choices like Mr., Mrs. or what not, and it not some large table, but only say 5-10 choices.
Anything larger? Use a data table driven listbox, and avoid use of value list.

how to write a script to look for and correct invalid entries in a table

First off I'd like to make perfectly clear that my knowledge of Access and VBA is extremely limited at best. I have an employee database system that due to it's age has been prone to small data corruption issues and controls breaking due to differences between 2003/2007 and 2010. While I've managed to hash out the bulk of the problems, one that has me especially concered is the script we're using to manage access to the database. The system is split between two files, a frontend where users can access the database and a backend file that contains all of the tables.
The issue I have is in the frontend form that handles the logon for the users. The way the access system is set up is the user enters their SSN, then the script finds their SSN in the table and if it exists looks if an access checkbox is checked. If they have access, they're directed to the main menu, if not they get a denied message. What I've found though is for some reason or another, if an entry in the personnel table has an incomplete SSN, the script breaks and anyone can gain access to the database.
There's a query that runs in the frontend that looks at the master personnel table and pulls just the first two columns, SSAN and Access.
The form itself has a visible text box, "Text8", and a hidden Combo Box "Combo4". Combo4 uses the previously mentioned query for the row source (SELECT qryAccess.SSAN FROM qryAccess;), while Text8 is where the user enters their SSN.
Here's the code right now:
Option Compare Database
Private Sub Combo4_AfterUpdate()
' Find the record that matches the control.
Dim rs As Object
Set rs = Me.Recordset.Clone
rs.FindFirst "[SSAN] = '" & Me![Combo4] & "'"
If Not rs.EOF Then Me.Bookmark = rs.Bookmark
If Me![Access] = True Then
DoCmd.RunMacro "Access"
Else
DoCmd.OpenForm "frmDenied"
End If
End Sub
Private Sub Text8_AfterUpdate()
Me![Combo4] = Me![Text8]
' Find the record that matches the control.
Dim rs As Object
Set rs = Me.Recordset.Clone
rs.FindFirst "[SSAN] = '" & Me![Combo4] & "'"
If Not rs.EOF Then Me.Bookmark = rs.Bookmark
If Me![Access] = True Then
DoCmd.RunMacro "Access"
Else
DoCmd.OpenForm "frmDenied"
End If
End Sub
Like I said before, as long as every entry for the SSNs is a full 9-digits, this system works. However, if for some reason the entry is not the full 9 like I just found in my database (and no, I have no idea what caused that to happen, there is an input mask in place, 000-00-0000;;_), this system breaks. You could type in "abc" for the SSN and gain access to the database.
How can I write a small script that pre-checks the table for SSN entries that don't fit the 9-digit format that is set, and if it finds them, resets them to an unused number, such as 000000000, 000000001, etc?
Also, if you have any suggestions on how to streamline the existing code, I'd be more than happy to take them.
Add this function to you application
Public Function IsValidSSN(ByVal SSN As String) As Boolean
'Determines if SSN is a valid social security number
'requires SSN to be in either "#########" or "###-##-####" format
IsValidSSN = (SSN Like "###-##-####") Or _
SSN Like ("#########")
End Function
Also change your function to this:
Private Sub Combo4_AfterUpdate()
' Find the record that matches the control.
If IsValidSSN(Me![Combo4]) Then
Dim rs As Object
Set rs = Me.Recordset.Clone
rs.FindFirst "[SSAN] = '" & Me![Combo4] & "'"
If Not rs.EOF Then Me.Bookmark = rs.Bookmark
If Me![Access] = True Then
DoCmd.RunMacro "Access"
Else
DoCmd.OpenForm "frmDenied"
End If
Else
DoCmd.OpenForm "frmDenied"
End IF
End Sub
Private Sub Text8_AfterUpdate()
Me![Combo4] = Me![Text8]
If IsValidSSN(Me![Text8]) Then
' Find the record that matches the control.
Dim rs As Object
Set rs = Me.Recordset.Clone
rs.FindFirst "[SSAN] = '" & Me![Combo4] & "'"
If Not rs.EOF Then Me.Bookmark = rs.Bookmark
If Me![Access] = True Then
DoCmd.RunMacro "Access"
Else
DoCmd.OpenForm "frmDenied"
End If
Else
DoCmd.OpenForm "frmDenied"
End If
End Sub
EDIT
Also why are you using a combobox to enter a SSN? You can use input mask on text box. Also I would highly suggest that you convert your system to some other identification other than SSN because it is easily passable to get past this code to look at the table containing everyones SSN, by holding down shift when opening the application. As for streamlining your code just remove that combobox altogether. If they are typing it into a textbox there is no need to put it into a hidden combobox.
You have a text field, SSAN, and with that input mask the dashes are not included in the stored values. So valid values would be 9 digit strings.
If that is correct, you can use a query to identify any invalid stored values.
SELECT y.SSAN, Len(SSAN) AS LenghtOfSSAN
FROM YourTable AS y
WHERE Len(SSAN)<>9 OR y.SSAN ALike '%[!0-9]%';
That query will return rows where SSAN includes < or > 9 characters, and any values which include characters other than digits.
Note the ALike keyword tells the db engine to expect ANSI wild card characters.  If you prefer Access' * wild card instead, change it to Like '*[!0-9]*'
Once you fix the stored values, add a Validation rule for that SSAN field (Like "#########") to require all values consist of 9 digits.
Since it looks like this became more of a "How do I find the user" than "How do I fix the existing entries", let me throw my hat into the ring.
Unless I completely misunderstand this, the existing (and accepted answer) function is HORRIBLE. You can do this all much more efficiently and with less code. First of all, delete Combo4. No need for it. Then do this:
Private Sub Text8_AfterUpdate()
Dim X as Integer
X = DLookup("Access", "qryAccess", "SSAN = '" & Me!Text8 & "'")
If Nz(X) = True Then
DoCmd.RunMacro "Access"
Else
DoCmd.OpenForm "frmDenied"
End If
End Sub
That's all you need. If the user's SSN was stored incorrectly, he's gonna be denied. 7 digits, 8 digits, doesn't make a difference. Only exact matches get through. That is, assuming 0 = False and 1 = True, which should be the default anyway.

Add Criteria To VBA Code and Requery

So the following VBA code I have will requery the form based on the Combo1 value equal to January. Eventually, I'll have X number of years in the Combo value and only want to display all records based on each year.
Is it possible to add additional VBA code to use the Criteria from the query instead of having X amount of queries and requery them based on the year.
Private Sub Combo1_AfterUpdate()
If Combo1.Value = "2013" Then
[Form_Main Form].RecordSource = "Main_Form_Query"
[Form_Main Form].Requery
End If
End Sub
You can construct the query in runtime (remember: a query object in access is just an sql statement)
So, let's asume that your data is stored in a table called tblMyData that has columns called month (String, month name) and year (Numeric, Integer) (just examples). You can generate the SQL query in VBA this way:
...
Dim strSQL as String
...
strSQL = "SELECT * FROM tblMyData " & _
"WHERE month='" & ComboMonth.Value & "' " & _
" AND year=" & ComboYear.Value ";"
...
[Form_Main Form].RecordSource = strSQL
[Form_Main Form].Requery
...
Hope this helps you.
Open the form in a different way.
DoCmd.OpenForm "MyForm",,,"MyYear=2013"
Or
DoCmd.OpenForm "MyForm",,,"MyYear=" & Me.MyCombo
Assuming that MyYear in the table or query is numeric. If it is not, you can use single quotes.
The general idea is that you use a form with all records, and use the Where argument of OpenForm to specify the records that should be shown. You can do the same with reports.
I have only a vague idea about what you're trying to accomplish. It might help to show us the SQL from Main_Form_Query. Meanwhile, consider whether you can use the form's .Filter property to do what you need.
If the form's Record Source includes a text field named fiscal_year, you could filter the rows displayed based on the year selected in your combo box.
Private Sub Combo1_AfterUpdate()
' .Value is the default property, so not required after Me.Combo
Me.Filter = "fiscal_year = '" & Me.Combo1 & "'"
Me.FilterOn = True
End Sub
Yet another approach could be to reference the combo value in Main_Form_Query.
WHERE
Forms!YourForm!Combo1 Is Null
OR some_field = Forms!YourForm!Combo1
Then do Me.Requery in the combo's After Update event.

Table completely ignoring variable

Private Sub Form_Current()
Dim bytoffcut As Byte
Dim strCriteria
strCriteria = "[WOID] = " & Forms![frmAddStockBooking]![MouldWO]
bytoffcut = Nz(DMax("OffcutNo", "dbo_tblOffcuts", strCriteria), 0) + 1
MsgBox bytoffcut
Me.txtOffcut.Value = bytoffcut
Me.WOID.Value = Forms![frmAddStockBooking]![MouldWO]
Me.txtdate.Value = Now()
End Sub
Can anyone tell me why this is not working? The variable is behaving as expected where bytoffcut increments by one when i create a new record. But when I check the table the field bound to txtOffcut the field reads 1 instead of the incremented value.
EDIT: This code is being used in the On current property of the form. When I create a new record using a button on the form Dmax is used to find the highest offcut No value in a table and add one to it.
This appear to work in the form as the offcut no txtbox increments. But when i look at the table instead of having records with an increasing offcut no Instead all records read 1
Try sending your where clause in the DMax like this, assuming the WOID field in the table is an number type and not text or date.
"[WOID] = " & Forms![frmAddStockBooking]![MouldWO]
It would be better to evaluate your DMax() expression only once, especially if dbo_tblOffcuts is a large linked table without a usable index on [WOID].
If your DMax() expression can return a Null, use Nz() to transform the Null to zero. Then add one.
Dim bytoffcut As Byte
Dim strCriteria
strCriteria = "[WOID] = " & Forms![frmAddStockBooking]![MouldWO]
'Debug.Print strCriteria '
bytoffcut = Nz(DMax("OffcutNo", "dbo_tblOffcuts", _
strCriteria), 0) + 1
MsgBox bytoffcut
Me.txtOffcut.value = bytoffcut
This may not give you what you want when other users are editing dbo_tblOffcuts.
I have managed to solve the issue of multiple records being updated by creating a primary key for the table I am writing to.
I think that because Access could not uniquely identify the record it would edit all the records that met the criteria or something of the ilk. I am not entirely sure myself.

Checking if record exists using DLookup (With Multiple Criteria)

Im trying to create a function in my VBA where if the record they are trying to insert already exists however it returns a type mismatch.
EventCombo is a integer
MedalCombo is string
Private Sub MyCombo_BeforeUpdate(Cancel As Integer)
If Not IsNull(DLookup("RacerID", "Medals", "RaceID = " + EventCombo.Value _
+ " AND Medal = '" + MedalCombo.Value + "'" )) Then
MsgBox "Record Exists"
End If.
End Sub
What this does (or is supposed to do) is make sure no one else has the same medal in the same race.
What am I doing wrong?
With combo boxes in Access, you need to make sure that .value is really what you want. Often the first column is hidden which is .value while what is visible on the drop down box is not .value. When using a combo box to eliminate confusion I use the .columns property.
Also, to make sure the result from the combo box is a number and not text (as you did not use quotes in your example) I used the val() function to convert the combobox data to a number. If it already is a number, this will have no effect. Otherwise, if it is a digit stored as a string, it will convert it to a number. This might not be strictly necessary but it eliminates another possible problem. If the combobox column has a value which is some text which cannot convert to a number it will return 0 which you can test for in your code.
I cleaned up your code a bit with the following
I replaced the + with & like Remou said
changed .value to .columns(0). If the column you are looking for is not the first one, change 0 to the appropriate value
value() function
removed line continuation _. (Personal preference, feel free to ignore)
Private Sub MyCombo_BeforeUpdate(Cancel As Integer)
If Not IsNull(DLookup("RacerID", "Medals", "RaceID = " & Val(EventCombo.Columns(0)) & " AND Medal = '" & MedalCombo.Columns(0) & "'")) Then
MsgBox "Record Exists"
End If
End Sub