I have number of split forms in an Access 2016 database which are regularly used by various employees for data entry. It is important that users are able to see old records but are not able to edit them.
However, I want to allow users to edit records that have been made that day in case they notice an error in a record they have just entered.
My current approach is to set the AllowEdits property on the form to yes, then to override it for entries made on the same day with the following code
Private Sub Form_Load()
If (Me![rec_date] < Now()) Then
Me.AllowEdits = False
Else: Me.AllowEdits = True
End If
End Sub
I think there is a problem with the If criteria though, as all this currently does is prevent editing of all records.
For background [rec_date] refers to the date on which the record was entered.
Couple of things:
Use the "Current" event instead if you're working with record changes. Form Load only works when the form itself opens. If you change records it will do nothing for you.
Might as well move that Else statement down to the next line and indent correctly for readability.
Your conditional statement doesn't quite match what you told me. You're trying to match a specific day but you use a date time (now()) instead of date. You also use less than instead of equals. I would suggest using equals and the date function (note: doesn't use () and returns only the date portion of now() ).
Hopefully that helps! Here's my suggested code:
Private Sub Form_Current()
' Only allow editing of records created today.
If (Me![rec_date] = Date) Then
Me.AllowEdits = True
Else
Me.AllowEdits = False
End If
End Sub
An even shorter form as suggested by Mat's Mug
Private Sub Form_Current()
Me.AllowEdits = Me![rec_date] = Date ' Only allow editing of records created today.
End Sub
Related
I am trying to hide labels and my text boxes for a certain section of my report if there is a specific field that is null. for example, I have staff that works weekdays and or weekends but don't work weekends. I don't want weekend info to show on the report if you are only working weekdays. this also includes the labels associated with it.
here is my approach. abstract any calculations involved in calculating who works weekends to a set of public functions in a code module. You can refer to public functions inside a code module almost anywhere in Access.Then in either the report open event or the report load event, set the controls you don't want to show to be invisible. for instance:
Option Compare Database
Option Explicit
Private iworksweekends As Boolean
Public Function SetWorksWeekends(value As Boolean) As Boolean
iworksweekends = value
End Function
Public Function GetWorksWeekends(employeename As String) As Boolean
GetWorksWeekends = iworksweekends
End Function
Public Sub RandomlySetWorksWeekends(employeename As String)
If Rnd > 0.5 Then
iworksweekends = True
Else
iworksweekends = False
End If
End Sub
Private Sub Report_Load()
RandomlySetWorksWeekends 'realistic?
If GetWorksWeekends Then
Me.txtAge.Visible = False
Me.txtDate.Visible = False
Me.txtTime.Visible = False
End If
End Sub
One advantage of affectively adding properties to your database like this is code reuse. Once you have more than one version of a report Murphy says you will likely need the inconvenient version. Multiple parameters and multiple reports? You will probably want a form for customizing the reports. With properties you don't have to bother passing and parsing parameters between forms or far worse referring to controls between forms and reports. Here is one rather random example of creating a report based on criteria: https://www.youtube.com/watch?v=tguyxbpOXp0
edit: Access does have traditional data based conditional formatting. Conditional formatting in Access is done by selecting the control and selecting format on the ribbon. In Access 2016 you can only change the color or outline of the control. Excel has better conditional formatting options so I recommend exporting the report to Excel when data based formatting is needed.
This should be an easy one. This form is filtered by [Dismissed] = "N". When the user clicks the "Dismiss" button, the [Dismissed] field changes to "Y". After the requery, the form should then return to the same row where the user was previously at.
Private Sub DismissButton_Click()
Me!Dismissed = "Y"
MsgBox "Dismissed!", vbOKOnly
Dim GoBackToThisRecord As Integer
GobacktothisRecord = Me.CurrentRecord
Me.Requery
Set Me.CurrentRecord=GoBackToThisRecord
End Sub
However, even though the built-in help files say that CurrentRecord is a read/write property, I get an "Invalid use of property" error message on this last line.
After setting the [Dismiss]="Y", and requerying the form, how do I get the user back to his/her previous location in the form?
I don't understand how your solution can work if the form is filtered to a value that the edited record no longer matches -- if you're filtered on [Dismissed] = "N" then changing the current record's Dismissed field to Y should cause the requeried form to exclude the record you've just updated.
That aside, I would never do it the way you've done it, as Me.CurrentRecord returns a number representing the position in the record. Since a requery can cause the number of records to change (e.g., somebody else edits or adds or deletes a record causing it to be included/excluded from the form's recordset) and the position of the sought-for record to change, I would use the PK instead.
Dim lngPK as Long
lngPK = Me!MyPKID
Me.Requery
With Me.RecordsetClone
.FindFirst "[MyPKID]=" & lngPK
If Not .NoMatch Then
If Me.Dirty Then
Me.Dirty = False
End If
Me.Bookmark = .Bookmark
End If
End With
That won't deal with the filter issue, but I leave that aside, since it didn't seem to be the issue that I thought it would be from the description of the original problem.
Nevermind. Fixed it myself. The last line is now:
Me.Recordset.Move GoBackToThisRecord
The right way to move to the previous record, whether it is a new one or not, is
Me.Recordset.Move GoBackToThisRecord -1
I use this function:
Public Sub RequeryFormAndKeepCurrentlySelectedRecord(f As Form)
Dim Position As Long
Position = f.CurrentRecord
f.Requery
If Position > 1 Then
f.Recordset.move Position - 1
End If
End Sub
While I have my app running, I question the methodology, and wondering if there’s a “better way”…
Overall design is to allow editing 200-300 records from a gridview (phase1) using VB.Net. The database itself is on SQL Server. There are a number of columns a user will enter into an “application”, and there are several columns that will be edited/maintained by “office users”, if you will. There are several dates involved in this ongoing maintenance, and that’s where the first of my questions revolves.
I have found “solutions” on the internet that got the code working, but am questioning them…
Problem #1 I ran into – dates are NULL in the database, and in trying to read them in using a SqlDataReader led to errors (cannot assign NULL to a Date object). Ok, that led into using a ternary operator to use “IsDBNull”, and either assign the value read from the DB, or to assign DateTime.MinValue. Problem “solved”…
Problem #2 – using the above method now shows dates that are the minimum VB date value – showing actual dates in the fields the user is to edit – definitely not “user friendly”, nor what I want. The only solution to this issue was:
Convert dates from Date or DateTime objects into String objects. This would then allow me to be able to assign an empty string to the gridview in the case where the date was originally NULL in the DB, which had to be transformed into DateTime.MinValue (which could be tested), and then another ternary operator to assign either “ToString” conversion, or an empty string to the gridview field.
Ok – editing is now accomplished. I added some “ScriptManager.RegisterStartupScript” commands to allow testing the validity of the dates the user enters – all is well.
Problem #3 (or 4) – I now need to update the database with the data the user entered – PRESERVING THE EMPTY DATE STRINGS – and update the database (using parameters…) with NULLs back in those date columns. However, again – the date is a string, and is empty, so I had to assign to a “MinValue”, first, then another ternary operator to test each date against “MinValue”, and either assign the date, or a DBNull.Value…
Yes, I guess I could have come up with a number of different update strings (including dates in some, excluding in others), depending on whether or not a string/date was empty or not... But that will only lead to future bugs, so, I guess I’ll be keeping a series of ternary operators.
So, the code for beginning the edit process looks something like:
While sdr.Read
Dim _date1 As Date = If(IsDBNull(sdr("date1")), DateTime.MinValue, sdr("date1"))
.
.
.
‘ Now add them to a List of my Class:
appsList.Add(New AppClass(… _
If(_date1 = DateTime.MinValue, " ", _date1.ToString("MM/dd/yyyy")), _
… )
Now to get the data back from the gridview to update the database:
Dim _date1 As Date
' see if we can convert the various dates...
Try
' see if empty…
If ((CType((row.Cells(19).Controls(0)), TextBox)).Text).Length < 2 Then
_date1 = DateTime.MinValue
Else
_date1 = DateTime.Parse((CType((row.Cells(19).Controls(0)), TextBox)).Text)
End If
Catch ex As Exception
ErrFlag = True
ScriptManager.RegisterStartupScript(Me, Page.GetType, "Script", "alert(‘Date1 Date is not valid - enter as MM/DD/YYYY');", True)
End Try
.
.
.
Dim sql As String = "UPDATE [foo_bar].[dbo].[bar_foo] set date1=#Date1, …….)
cmd.Parameters.AddWithValue("#Date1", If(_date1 = DateTime.MinValue, DBNull.Value, _date1))
Honestly, all this conversion back and forth seems like it’s going to lead to bugs or errors at some point.
So – is this the “best” method for handling this? There isn’t a cleaner way?
If your using winforms then you can handle the Format and parse of the databindings. I haven't tried it on a Gridviewtextbox but worst case you can use a custom cell template and a textbox with format and parse handlers. The code would be something like this:
mybinding = New Binding("text", DataSet1, "table1.datefield")
Me.DataGridTextBoxColumn1.TextBox.DataBindings.Add(mybinding)
AddHandler mybinding.Parse, AddressOf StringToDateTime
AddHandler mybinding.Format, AddressOf formatdate
Private Sub StringToDateTime(ByVal sender As Object, ByVal cevent As ConvertEventArgs)
If cevent.Value.GetType().Equals(GetType(String)) And _
cevent.DesiredType Is GetType(DateTime) Then
If cevent.Value <> "" Then
' Make sure matches format in format funtion
cevent.Value = DateTime.Parse(String.Format(cevent.Value, "MMM d, yy"))
Else
cevent.Value = DBNull.Value
End If
'cevent.Value = DateTime.Parse(String.Format(cevent.Value, "MMM d yyyy")
End If
End Sub
Public Sub formatdate(ByVal sender As Object, ByVal e As ConvertEventArgs)
If e.Value.GetType().Equals(GetType(DateTime)) And e.DesiredType Is GetType(String) Then
' Hard-coded or user-specified
' Make sure matches format in parse funtion
e.Value = Format(e.Value, "d")
End If
End Sub
I have a combo box (Status) which includes the following:
Shortage
Allocated
Actioned
Acknowledged
Complete
I also have 5 other date fields which are as follows:
Shortage_date
Allocated_date
Actioned_date
Acknowledged_date
Complete_date
However I want this status to be populated automatically based on what data has been entered in my previous fields.
For example, once shortage_date has been populated with a valid date (00/00/0000) I want the "status" to change to "shortage".
Once allocated_date has been populated with a valid date (00/00/0000) I want the "status" to change to "allocated".
I saw this bit of code online but I'm totally confused:
Private Sub Textbox1_AfterUpdate()
If Textbox1.Value = "1" Then
Textbox2.Value = "10"
End If
End Sub
I believe mine should look something like this but I dont know what I need to make sure it validates the date.
Private Sub shortage_date_AfterUpdate()
If shortage_date.Value = "(I want to valididate the date here)" Then
Status.Value = "Status"
End If
End Sub
Hope I make sense!
Firstly, I would set up an Input Mask on the field itself. You can do that by putting the form into design view, then select the field, then go the Property Sheet which isAlt+Enter if it isn't open, then select the Data tab and set up a Input Mask. That will handle your validation part so you don't have to in code.
Then you should be able to just use the code:
Private Sub shortage_date_AfterUpdate()
If Nz(shortage_date.Value, "") <> "" Then
Status.Value = "Status"
End If
End Sub
The If statement is just to make sure that it doesn't reset the value back to the original every time the date is changed. Also here is link where you can read about Input Masks: https://msdn.microsoft.com/en-us/library/office/ff821336.aspx
Update: Changed to Input Mask instead of Validation Rule
I am requesting some guidance to fix this problem I have been having while trying to edit the "AfterUpdate" event in my database. It seems to work fine on my local machine but when I try to implement it in the network database it doesn't work properly (occasionally puts in a random date in one of the fields, doesn't erase the date when I supply a new ID, etc.)
Here is the VBA Code I have:
Private Sub provider_surveyID_AfterUpdate()
provider_survey_dueDate = DateAdd("ww", 2, DLookup("completed_on",
"qry_ProviderSurveyInfo", "provider_surveyID=" & provider_surveyID))
provider_survey_reminder2weeks = DateAdd("ww", 4, DLookup("completed_on",
"qry_ProviderSurveyInfo", "provider_surveyID=" & provider_surveyID))
provider_survey_reminder4weeks = DateAdd("ww", 6, DLookup("completed_on",
"qry_ProviderSurveyInfo", "provider_surveyID=" & provider_surveyID))
End Sub
The query is correct, joining the Survey ID to the appropriate participant to determine the original "completed_on" date that is used in the DateAdd functions.
Can you see any reason why it would not work as it does on local - removing dates when entering IDs currently not in use? Also, think it would be wise to use Nz(provider_surveyID,0) in this instance? I haven't implemented it yet as I wanted to make it work appropriately as it does on my local with no issues whatsoever - I enter for the ID, it populates; I enter 20 (not in use yet), it makes all dates null again.
Dlookups can be slow and tricky to use, it will also return random values when the Criteria isn't entered. I'm not sure on how your form is put together (I'm assuming it's a form), and this may be a partial answer:
Private Sub provider_surveyID_AfterUpdate()
Dim dtComplete as Date
If IsNull(provider_serveyID) then
provider_survey_dueDate=""
provider_survey_reminder2weeks=""
provider_survey_reminder4weeks=""
Else
dtComplete = DLookup("completed_on","qry_ProviderSurveyInfo", "provider_surveyID=" & provider_surveyID)
provider_survey_dueDate=DateAdd("ww",2,dtComplete)
provider_survey_reminder2weeks=DateAdd("ww",4,dtComplete)
provider_survey_reminder4weeks=DateAdd("ww",6,dtComplete)
End if
End sub