VBA/Access: Track the last date a macro was run via a command button - vba

I'm using command buttons to import excel files into various tables, and I want to track the last time each table was updated (i.e. the last time the command button was clicked and ran successfully).
I've tried doing this by adding a text field next to each command button, and then adding Me.TextField1.Value = Date to the _Click subroutine for each button. This fills in the date correctly, but it doesn't save the value when I close the data base.
How do I keep the date value from resetting every time I reopen the database?

The way I would probably approach this is to add a table to your database that contains a list of all the Excel files you import on a routine basis. You would store the filename of the Excel file in one column, and the updated date in another column.
Then you can use the DLookup() function to pull the updated date into your text box by your command button.
Of course there are many approaches you could take here, but which one is best depends on your specific needs. If it is just you running this application on a single computer, you might find it easier to use the SaveSetting/GetSetting functions to save simple values to the registry.
SaveSetting "ExcelImporter", "sheet2.xlsx", "Updated", Now()
TextField1 = GetSetting("ExcelImporter", "sheet2.xlsx", "Updated")
If you have a lot of spreadsheets, you might consider a list box with two columns, one with the name of the file, and the other with the date it was last updated. Then you could use a single button to run the update, based on the selected file. Lots of options... :-)

Add a Date/Time field to the table you are updating and set the Default Value property of the field to Now(). When new records are inserted, they will automatically have their insert date and time set.
If you are updating existing records, then update this field to Now explicitly.
You can query this date with
Dim lastUpdate As Variant
lastUpdate = DMax("DateFieldName", "TableName")
In addition, you see when each record has been inserted or updated.
TextBox values are not saved permanently, unless the RecordSource of the form has been set to a table or query and ControlSource of the TextBox has been set to a column of the table or query.

You could create a Custom Database Property to hold the last time the button was clicked.
Function getLastTimeClick() As Date
Const PropertyName As String = "LastTimeClicked"
On Error Resume Next
getLastTimeClick = CurrentDb.Properties(PropertyName).Value
If Err.Number = 3270 Then
CurrentDb.Properties.Append CurrentDb.CreateProperty(PropertyName, dbDate, Now)
End If
CurrentDb.Properties(PropertyName).Value = Now
On Error GoTo 0
End Function

Related

MS ACCESS: VBA too fast leading to field updating only if Toggle Breakpoint in code, need timer?

I am currently doing a Purchase Order "software" under Access and I am having issue with calculating the amount after a item quantity (qty) update.
When I have Toggle breakpoints in my VBA code on the "after update" event, after updating qty = 5 and going through the lines of code, I have the parent text field correctly updated from the subform one (which is the sum of the 'Total Price' fields).
The code contains a Refresh in order to add the right value rather than the old one:
.
.
However, when I remove the toggle breakpoint, I think that the VBA code does not have enough time fully process the refresh command, which update the parent text field with the wrong (old) value.
This issue leads to have a discrepancy between the actual subform calculated total, and the value added to the parent table (here I removed the toggle breakpoint, and added 1 on the second line item, but the calculation gave a 250 rather than a 251):
When I look at the values in the code, when the breakpoint is on the updating line, you can see that the value of the field is the right one, but the 'watch' field shows the old value:
.
.
Do you guys have a solution to make sure all the fields are updated before going to the next line?
I was thinking using some sort of delay, or an "application wait until processing done" type of command but I cannot find anything that is actually working;
Let me know,
Cheers!
EDIT 1:
The "Expected Total Cost" is bound to a table field called "curPOExpectedTotalCost", which is why I use VBA code to populate the data into its dedicated textbox (called "txtcurPOExpectedTotalCost").
The main goal is simply to have this bound field being correctly updated; I want to be able to change the qty or the unit price and automatically populate the right total PO price into "Expected Total Cost" which is bound to a table; the issue is that it works well when I am running each lines one by one using the breakpoints in my code, but does not work when I remove them; this tells me it is probably too fast, hence a way of delaying the next command or a command to wait for processing to be done.
EDIT 2:
I found a workaround, but it seems overpowered for this simple task I was trying to achieve; good side is that it removes the middle man (the subform textbox that sum all the total prices):
I open a recordset and iterate a variable until I can populate the result into the dedicated "Expected Total Cost" bound textbox:
DoCmd.RunCommand (acCmdRefresh)
Dim RS As DAO.Recordset
Dim SQL As String
SQL = "SELECT numPONumberAndRevID, numPOContentQtyOrdered, numPOContentPrice FROM tblPOSCONTENT WHERE numPONumberAndRevID = " & Nz(Me.Parent.MasterPOID.Value)
Set RS = CurrentDb.OpenRecordset(SQL)
Do While Not RS.EOF
ExpectedCalculatedCost = ExpectedCalculatedCost + RS("numPOContentQtyOrdered") * RS("numPOContentPrice")
RS.MoveNext
Loop
RS.Close
Set RS = Nothing
Me.Parent.txtcurPOExpectedTotalCost.Value = ExpectedCalculatedCost
We could arrange in a simple way.
We name the detail input subform control as sfrmDetail, the main total control can be assigned with a .ControlSource like:
Me.txtcurPOExpectedTotalCost.ControlSource="=[sfrmDetail].[Form].[txtSubFormExpectedTotalCost]"
When txtnumPOContentQtyOrdered updates, your main form changes instanteneously without any VBA code.
In the subform, say sfrmDetail, that will be embedded in the main form as subformcontrol sfrmDetail (yes with the same name), we assign a .ControlSouce=Sum(Qty*UnitPrice) for the control txtSubFormExpectedTotalCost, at the footer of the subform.
Solution II:
As #June7 pointed out in comments, the Total cost, that can be calculated dynamically, should be better only for display only (form input display, report printing, but not saved in disk).
OK, now we want at any cost to stock in field tblMain.curPOExpectedTotalCost the total cost with a slight data redundancy, so in the main form we have Me.txtcurPOExpectedTotalCost.ControlSource="curPOExpectedTotalCost"
In the subform sfrmDetail, we can update with:
Option Compare Database
Option Explicit
Private Sub txtnumPOContentQtyOrdered_AfterUpdate()
Me.Recalc
Me.Parent.txtcurPOExpectedTotalCost.Value = Me.txtSubformExpectedTotalCost.Value
End Sub
Me.Recalc() updates all calculated fields of sfrmDetail before we change the target main table field.
Please consult also Is storing counts of database record redundant? for data redundancy.

Microsoft Access VBA If statement

I have a dropdown menu that has three options "Open, Assigned, Closed" I have three rows in a table in my database, one for openTime, assignedTime, and closedTime.
My dropdown menu is connected to a row in my table called Status which is swapped between the three options "Open, Assigned, Closed". When the ticket is created in my website, it creates an open time so I don't have to worry about that, but when the user selects assigned and closed I would like it to on change send data to the correct cell.
So if the drop down is swapped from open to assigned it already changes the status cell to "Assigned" in my database but I would also like it to change the assignedTime cell's data to the exact time and date. Same for closed.
I think this is simple I'm just a bit confused on the process. Im using this site for info on IF statements
I have my code that creates and adds my date
me.MyDateTimeColumn = now()
I have a few questions
1) In my IF statement how do I say the value of the selection for example if the user selects "Assigned' in the drop down menu how do I say thats the info I want my IF statement looking at?
On the site linked above this is there example for an if statement:
Sub AlertUser(value as Long)
If value = 0 Then
AlertLabel.ForeColor = "Red"
AlertLabel.Font.Bold = True
AlertLabel.Font.Italic = True
End If
End Sub
On this do I need to include the Sub AlertUser(value as Long)(using my own variables) If so what would my sub be?
and does the If value = 0 Then refer to the value of the selected comboBox?
2) Can I insert an else if as I would in other languages, do I need an end else if?
I am using a microsoft access form, and using the VBA code section for this. MY database is in a sql server connected to the form.

Access VBA Combobox Store Value in Column

I have an MS ACCESS Combo Box and I wish to change the value of one of the columns in a particular row. I get error "object required" when I run this line:
Me.ComboName.Column(12, intUseRow) = myVar
(If I am unable to use the above syntax then you should also know that the row I am trying to change is always going to be the "current" visible row so there may be another way of solving the problem due to this fact).
Thanks!
If you have a recordset that is bound to a Table/Query, you will need to change the underlying data then requery the combobox to see changes.
If you load it manually (like in the form load event) and have the comboBox Row Source Type to "Value List" - you should be able to update it like this:
Copy all the data from the selected row into variables.
Combobox.RemoveItem (selected index)
change the required variable to the new value.
construct the semicolon separated string for the value list entry
combobox.AddItem new-string.
a bit messy, but it works correctly!

MS Access - Disable Update on a Certain Field or Column

How do we disable update or edit on a certain column for a record. For example I have Product table with fields of ID, Description, Count. I want to disable the changes on Description only. I know how to do this in sql, but how about in a program for Access or VBA code?
If you're using a form to present the data using a text box control bound to the field, you can lock the field for edits in the field properties. The property is called "locked".
You can also use vba code to lock and unlock the field under certain conditions.
Sub form_current ()
If x = "superuser" then
Me!SalaryField.enabled=true
Me!SalaryField.locked=false
Else
Me!Salaryfield.enabled=false
Me!Salaryfield.locked=true
End if
End Sub

MS Access Can't go to specified record because of another control's VB where clause

I have a lookup listbox which is programmed to allow the user to find a specific record/help topic from the list and view it. Now when the list box is used the where clause locks in the record and the first, previous, next, last buttons freeze up and you cannot use them to go to a record. Is there a way to free up the functionality of the buttons to navigate through the records along with the where clause to select.
Here is the code that operates the listbox selections:
Private Sub List35_AfterUpdate()
Dim myTopic As String
myTopic = "Select * from FormsHelpTable where ([ID] = " & Me.List35 & ")"
Me.Form.RecordSource = myTopic
Me.Comment.Requery
End Sub
I believe since the where clause locks in the selection in the box it does not allow navigation from other controls to interfere. What might be a way around this?
You get the runtime error:
You can't go to specified record.
It appears not to be reading the other record in the source table named Help once it updates using the listbox.
Instead of changing the recordset (this is the 'pool' of records which the form could display), you just need to go to the record the user selects from the listbox.
Method 1 (probably the easiest way if your listbox is in the same order as the records of your form)
Use this code:
Private Sub lstSelect_AfterUpdate()
DoCmd.GoToRecord acDataForm, "HelpForm", acGoTo, Me.lstSelect.ListIndex + 1
End Sub
You need to ensure that:
The recordset of the form is ordered the same as the listbox. So, for example, you could order both by ID, or by Title.
Note that the +1 comes from the fact that the ListIndex starts at 0, whereas the record indexes start at 1.
Method 2
Ensure each record's Title is unique (set No Duplicates on this field).
Change your listbox to return the Title of the record, rather than it's ID.
Then use this:
Private Sub lstSelect_AfterUpdate()
Me.Title.SetFocus
DoCmd.FindRecord Me.lstSelect
Me.lstSelect.SetFocus
End Sub
Things to note:
It works by searching the field with focus for the string specified. That's why we have to SetFocus on the Title textbox on our form.
We could use ID instead, (which would mean we could have duplicate titles if we wanted), but then we would have to have an ID control on the form to SetFocus to. You can't hide this control either, because it needs to have focus whilst using FindRecord.
Update: Method 1 with reverse-selection
Add an Event Procedure in the Form_Current event, with this code. Then update the code in the lstSelect_AfterUpdate procedure as shown after.
Private Sub Form_Current()
Me.lstSelect = Me.lstSelect.Column(0, Form.CurrentRecord - 1)
End Sub
Note that, depending on how your lstSelect is set up, it may be Column(1, Form.CurrentRecord - 1) instead. Read on for details!
Private Sub lstSelect_AfterUpdate()
DoCmd.GoToRecord acDataForm, "HelpForm", acGoTo, Me.lstSelect.ListIndex + 1
Me.lstSelect = Me.lstSelect.Column(0, Form.CurrentRecord - 1)
End Sub
Explanation of new lines:
The Form_Current event fires every time we go to a new record. We need to look at the index of the record (ie. the position of it in the recordset), which we get by using Form.CurrentRecord. We then want to make the listbox select that record. However, we can't use me.lstSelect.ListIndex as before, because that is a read-only property (we can access it to read it, but we can't set it).
Therefore, we use me.lstSelect.Column(colNum,rowNum) instead, where we specify a column number and a row number. If your listbox has two columns (eg. ID and Title), we want to choose the second column. The index starts at 0, so we would use a value of 1. If, like my lstSelect, you only have one column (Title) then we use 0. Note: it doesn't matter if a column is hidden (ie. has width 0). It still 'counts'.
The row number is Form.CurrentRecord - 1. Remember that the forms recordset index starts at 1, but the index of our listbox starts at 0; hence the - 1.
So why do we need a duplicate of this new row in the AfterUpdate event? Try and comment it out to see what happens if we don't put it in. It's has to do with the Form_Current event firing after we use the listbox.
I fixed this issue with a union clause in the SQL lookup code. The UNION ALL clause and the following union on the table used in the other part had allowed all the records to be used.