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

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.

Related

Why does MsAccess Record entry only Update (and show in table) after form close and reopen?

Some Background:
I have a database that acts as a ledger (keeps tabs on current payments).
1:Payments for each customer are stored in one table (PaymentTbl),
2:I have another table (TermAgreementTbl) that holds information on the agreed terms of the service
3: My last table (PdUpToTbl) takes the payment information as well as term agreements information from the two other tables and calculates/displays the information in a clearer manner. One of the ways it will do this, is by deleting the last record in PdUpToTbl, and replacing it, or by just adding a new record (case dependent).
Now MY Issue:
I have a form for my TermAgreementTbl that has a subform showing the relevant PdUpToTbl.
A button opens a pop-up form to enter a new payment and update the related PdUpTotbl.
Everything in the back end is functional, however after I enter a new payment (and save and close the pop-up payment form), NO new record is shown in my PdUpToTbl Subform. Instead it shows something like (some irrelevant info redacted):
For the new record to display properly, I have to close the entire form, and reopen it. There has got to be a way to get around this through vba with some code, right?
Thank you for taking the time.
Edit 1:
By the way, after I perform A LOT of vba code, I use this to enter my record:
With pdUpToRS
.AddNew
![DatePaid] = NewRecordSet.Fields("DatePaid").Value
![Amount] = Amount
![AppliedAmount] = AppliedAmount
![OnAcct] = OnAcct
![AllPdUpTo] = AllPdUpTo
![RemainBalDue] = RemainBalDue
![PdUpToString] = PdUpToString
![PaymentType] = NewRecordSet.Fields("PaymentType").Value
![PaymentNumber] = PaymentNumber
![ID] = NewRecordSet.Fields("ID").Value
![PmntTblID] = PmntTblID
![BdCk] = BdCk
![Late] = Lte
![ApplyDiscount] = ApplyDiscount
![ForgetUnderage] = ForgetUnder
![ForgetOverage] = ForgetOver
![Note] = Note
.Update
End With
Update using requery
I have tried to Requery using:
Forms![MainForm]![Subform].Requery
But it gives me the error:
2118 - - - You must save the current field before you run the Requery action.
And if I add the save line:
DoCmd.RunCommand acCmdSaveRecord
Forms![MainForm]![Subform].Requery
I get the resulting error:
2046 - - - The command or action 'SaveRecord' isn't available now.
Ok, the docmd "menu" options to save a record?
They OFTEN run on the form that has the current focus - so often, then the form you want to save is not the one you expected.
I suggest you replace this:
DoCmd.RunCommand acCmdSaveRecord
with
if me.Dirty then me.Dirty = false
Now, above above "me" is is the current form WHERE that code is running, not some form that might happen to have the focus.
Now, as for a form "requery" (to refresh without close then re-open)?
Again, assuming the above just did the save of the data, then to force a re-load of the current form (again, the form in which the code is running), then:
me.Requery
In fact, if you did not have multiple sub-forms, then a me.refresh would probably work (and a me.Refresh will also save the current record).
So, while the if me.dirty = true then me.dirty = false is about the best way to save the current reocrd in the current form where the code is running?
It is a question of where your code is running, and often when the code is running.
In place of the me.dirty = false, you can also do this, but it often will cause a lot more flicker and refreshing then you want.
But, the shortest code to ensure a save of the forms data, and then requery (same as form close then open), would thus be this:
Me.Refresh
me.Requery
However, often issues can arise if you have some dialog form open - so perhaps a closer look at how your code is updating is often imporant.
But, a me.Requery will re-load everything, but you of course want to ensure you force record saves of the data first.
I think the Requery function will serve you well.
https://learn.microsoft.com/en-us/office/vba/api/access.subform.requery

How would I set focus in a LibreOffice macro (for use in a Base form)? Or set up a "Position-To" field that DOESN'T filter?

In a LibreOffice Base form, I have a button set to call .uno:RecSearch, in order to bring up the record search dialog.
The problem with that is that if no field in the form has focus, the record search dialog comes up with the third of seven date fields in the form as the search target (which is pretty much useless), rather than the desired "Name" field. (And even more puzzling, the listbox to select which field to search seems to have the fields in more-or-less random order).
I would like to create a macro for a LibreOffice Base form, that would first set focus to the "Name" field, then call .uno:RecSearch.
Can that be done, and if so, how?
I never did find a way to do that (although I found that the sequence of fields as seen by macros bears no resemblance to the nominal sequence of the fields in the record or on the screen, which is a puzzle in itself), but I found something that, for my purposes, is better: how to implement a "Position To" field (as contrasted with a filter, which is precisely what I don't want!) with a macro, attached to the "Changed" event of a "Position To" field added to the form.
option explicit
sub PositionTo (e as object)
dim c as object
dim oDoc, oDrawpage, oForm, oName, oConnection as object
oDoc = thisComponent
oDrawpage = oDoc.drawpage
oForm = oDrawPage.forms.getByName("MainForm")
oName = oForm.getByName("txtName")
if ucase(e.source.text) < ucase(oName.text) then
do until (ucase(e.source.text) > ucase(oName.text)) or (oForm.isBeforeFirst)
oForm.previous
loop
oForm.next
else
do until (ucase(e.source.text) <= ucase(oName.text)) or (oForm.isAfterLast)
oForm.next
loop
if oForm.isAfterLast then oform.previous
endif
end sub
It is, of course, a brute-force approach, that would have serious performance problems in a database with more than a few thousand records, but then again, a database with more than a few thousand records would call for something more sophisticated than LibreOffice Base over its own built-in (and rather out-of-date) HSQLDB.
The situation is a table, in which the unique primary key is a non-case-sensitive text field.
First, we define our variables, and get hold of the form and the keyfield's presence on the form. Then, we check whether the value of the "Position To" field ("e.source.text") comes before the current record's key value ("oName.text"), ignoring case.
If we're after the Position To value, then we step backwards through the records until we get to a record before our "Position To" value, or we're at the top of the file, then step forward one record.
If we're before the Position To value, then we step forward until we reach the first record that's at least a match, or we hit EOF; if the latter, we back up to the last record.
Performance could of course be improved for long sweeps by doing it in two stages, with longer steps for the first stage, but that would be more trouble than it's worth, given the size of the database.
But of course, I would welcome any viable, practical, suggestions for improvements. And if there's an existing "position to" function I don't know about, that would help.

MS Access Database form not updating after SQL requery

I have a report that runs from a query. The query does use a global variable but this is not the problem but needed for the explanation. The function for the variable is:
Function Var1() As String
Var1 = strVar1
End Function
The query where statement is:
WHERE (((IIf([MinOfDueDayMin]<0,0,Int([MinOfDueDayMin]/7)+1))<Var1()+1) AND ((tblEquipment.Retired)=False))
which uses the var1 function
The criteria is on a field that is actually a calculation and that is where I think the problem starts.
The report is run for a command on another form using the following code:
strVar1 = InputBox("Enter Number of Weeks for report")
If strVar1 = "" Then Exit Sub
DoCmd.OpenReport "rptEquipPmSchedule", acViewReport
Everything works just fine
On the report I have a double click event that opens a form. This form uses part of the same query. (not the same one but two levels higher) thiS allow the user to change things so i expect to use requery for the report.
If i double click and then not even change anything and then go back to the report I have #ERROR in the fields that have the calculations
i put a me.requery in the activate event of the report. this did not work.
So I tried a work around.
When I double click the report field, i close the report and send the strVar1 value to the form that is opened. then when I close the form I reasign the strVar1 just in case it is lost be an assignment by another user (currently I am the only one using this but did it just to be sure it had the correct value.) Then I open the report again but still get the errors. I did not expect this at all. thought starting the report from scratch would certainly work. I even closed the form just after assigning strVar1.
then in final effort. When I close the form I run the exact same code:
strVar1 = InputBox("Enter Number of Weeks for report")
If strVar1 = "" Then Exit Sub
DoCmd.OpenReport "rptEquipPmSchedule", acViewReport
Which will force the user to input the value for strVal1. Even though this is not what I want but tried this for troubleshooting and I still get #ERROR.
When I run the report for a form that does not have any of the same field, no issues. When I run the report or keep it open with a requery from the form that has the same data, the report will not give the correct results. Note that if I run the query itself, the data in the query is correct.
i also tried using a number instead of Val1() in the query and got the same results.
i also tried the refresh button in the ribbon and get Unknown Function Name and all the data in the report is lost.
Anyone got any ideas??
While your textual explanation is difficult to understand the entire scope, consider re-assessing your workflow. The entire objective is to allow users to run customized criteria for reporting. And your main issue is the strVal does not persist in memory so all references to it fails.
Consider the following points:
Have users set criteria on a dedicated unbound form with button click for report where that report instance is immutable for viewing/printing only and if needed to be changed must be re-run (i.e., button re-clicked).
Access has no need for VBA's InputBox() as strVal can be an unbound textbox on this unbound form whose value remains intact for all open windows.
Have function and all its references point to form field: Form!myFormName!strValuetextbox
Because reports on pretty much any software/web app system is not used as a GUI interface to run actions, users will know if they intend to change report criteria, close current report or go back to entry point and change strVal then re-click button to re-run report.
Keep data entry/input (primary use of forms) separate from data export/output (like reports). From developer and user standpoint this compartmentalization will save you headaches down the road.

How to work around Access VBA error 3188

Having converted a number of fields in a table tblSource to Rich Text memos, I'm getting an error 3188 in the following circumstances.
Main form has a subform open (frmSource) bound to qrySource. qrySource pulls in some fields from tblSource and adds a calculated field which concatenates the (newly-minted) rich-text memo fields (SD1 to SD20) so that the result can be displayed in a single text box on frmSource called Citation.
If the user wishes to edit SD1 to SD20, they double-click on the Citation field and a modal form frmCitation opens up displaying the SD fields for editing. frmCitation is bound to qryCitation which pulls the SD fields and a couple of others out of tblSource. When finished, they close frmCitation. When SD1 etc were text fields, the tblSource record was updated successfully. However, now they're memo fields, I'm hitting VBA error 3188 ("Could not update; currently locked by another session on this machine.").
Searching on the Internet suggests that this is a common issue with Rich Text memos when a memo size exceeds 2k (limitation possibly due to Access edit buffer size?), so I'm looking for ways to work around it.
One option would be be to split tblSource into two tables tblSource and tblCItation with a one-to-one relationship between them, then base qrySource on tblSource and qryCitation on tblCitation, but that's fairly major surgery with knock-on effects in a number of other places in the application.
Another option is to limit the size of all the memo fields on this form (as per Rich text input into limited length text field in Access 2010), but there's one field for which that wouldn't be acceptable to the users.
Is there another technique I could explore?
Per the following MS Link: http://answers.microsoft.com/en-us/office/forum/office_2007-access/memo-field-could-not-update-currently-locked-by/d5c8163a-7ce5-484f-80d4-98c1a8c92160
near the bottom, they suggest the limit is 2,000 characters.
Not that it helps, but could you add code to the 'before update' to display what the new size should be wnen concatenated? May help to figure a solution...
Be sure to save the form before making any changes with:
If Me.Dirty Then Me.Dirty = False
Workaround 1: Try to update not a field of an underlaying query or table, but a field of the form's Recordset. For example:
With Me.Recordset
.Edit
!MemoField = "TEXT ADDED HERE <- " & !MemoField
.Update
End With
Workaround 2: If you need to update not form's Recordset, but any other Recordset, try to unbind the TexBox, make the update and then make the TextBox binded back again.
I had the same issue and I closed all other open tabs (tables, etc...) and tried again. That worked fine.
I had this problem years ago & was revisiting in case it as not an issue anymore, but seems it is.
I had a list/column form of emails inc a field with a large rft memo.
I wanted to click on a record to bring up the one email in single form
The only way I could get it to work was Open the Single form & immediately close the multi row form, on exiting the single had to load the list again using bookmarks etc. to return to where I wanted to be, a real pain.
Especially as like now I would like to be able to access the the list whilst still on the single form, hence the revisit, instead looks like I will have to save all the list'id somehow. Dooable but a lot more programming that I think should be necessary.

Access 2007: Filtering a report's results using a drop-down box

My question is twofold.
I have around twenty assorted tables in a database. The table layouts are diverse; the one common thread is that all of them have a 'County' field.
I need to set up a series of reports which allow a user to select a county from a drop-down box, triggering the report to run and return only records attached to that particular county.
This is doable at the datasheet level using a filter-by-form, but that's pretty clunky and I have several tables/queries which will need this same county filter.
I may be halfway there with the following:
Create an unbound form.
Add a combo box.
Set the Row Source of the combo box to include the County field.
Set its Bound column to 1.
Set its Column Count property to 2.
Set the Column Width property to 0";1"
Name the Combo Box 'ChooseCounty'.
Add a Command Button to the form.
Code the button's click event as follows:
(Note: To write the code, in Form Design View select the command button. Display the button's property sheet.
Click on the Event tab.
On the On Click line, write:
[Event Procedure]
Click on the little button with the 3 dots that appears on that line.
When the code window opens, the cursor will be flashing between two already existing lines of code.
Between those lines, write the following code.)
Me.Visible = False
Close the Code window.
Name this form 'ChooseCounty'.
In the Query that is the Report's Record Source [County] field
criteria line, write:
forms!ChooseCounty!ChooseCounty
Next, code the Report's Open event:
(Using the same method as described above)
DoCmd.OpenForm "ChooseCounty", , , , , acDialog
Code the report's Close event:
DoCmd.Close acForm, "ChooseCounty"
When ready to run the report, open the report.
The form will open and wait for the selection of the Company.
Click the command button and then report will run.
When the report closes, it will close the form.
I can persuade the report to trigger the form, but only once - I can't seem to figure out where precisely the 'forms!ChooseCounty!ChooseCounty' needs to go. Perhaps someone can clarify or offer a more elegant way to do this?
I need to set up a large meta-report containing sub-reports on all of the tables - and, using the same drop-down 'choose a county' form, I need to have that choice cascade down through all the subreports. I don't have the faintest idea how to go about this. Suggestions welcome!
~ T
You seem to be asking two questions, the last of which is clear to me, but the first is not. The second one is in regard to how to cascade the filter to the subforms. You can do this in one of two ways:
put the form control reference as criterion in the recordsource of each subreport, OR
create a non-visible control on the report that has as it's controlsource "=Forms!ChooseCounty!ChooseCounty". Name that control "CountyFilter". Then, add CountyFilter to the link properties. If, for instance, you are linking the subreports on ID, you'd have:
LinkMaster: ID;CountyFilter
LinkChild: ID;County
(assuming, of course, that ID is your link field for the child reports, and that "County" is the name of the field in the child subreport).
Now, I'm wondering why you would have the County data not just in the parent record but in the child records -- that makes no sense. If you do have it, then the solution above will work.
If you don't, then I don't understand the question, as the whole idea behind subreports is that they are filtered by the parent record, so if the parent record is a person, and you filter by COUNTY, you're only going to get the child records in the subreport for that person, which by definition are already filtered by COUNTY because the parent has been filtered.
As to the earlier question, you write:
I can persuade the report to trigger
the form, but only once - I can't seem
to figure out where precisely the
'forms!ChooseCounty!ChooseCounty'
needs to go
You have two choices:
hardwire the recordsource of the report to use the form control reference, so the WHERE clause of your report would be "WHERE County=Forms!ChooseCounty!ChooseCounty" (and you should set this as a parameter of type text to insure that it gets processed correctly).
the better meethod is to set the recordsource in the report's OnOpen event.
After you open the form as a dialog, you'd have something like this:
Me.Recordsource = "SELECT * FROM MyTable WHERE County='" _
& Forms!ChooseCounty!ChooseCounty & "'"
And immediately after that line, you can close the form, since it's not needed any longer.
You will likely want an OnNoData event for the case where no records are returned. This is usually something simple like:
MsgBox "No records found!"
DoCmd.Close acReport, Me.Name
I hope this answers your questions, but if not, I'm happy to offer more explanation.