(Access) search text field for strings matching those in another table's field and display these matching records in a subform - vba

Here's my situation,
tbl_products / frm_products
ID__products
products__name (short text)
products__description (short text)
tbl_articles / frm_articles
ID__articles
articles__name (short text)
articles__body (long text)
I have a form bound to tbl_articles containing a subform bound to tbl_products.
What I would like to happen is,
once a user enters text into the articles__body field, (using the After Update module)
it searches this long text field for any words which match a product name
and then (in the subform) displays the matching products.
For example if the articles__body record that the user is currently viewing contains 'product 1', it will display product 1's record in the subform.
Perhaps After Update is not appropriate as it needs to appear to remember these matches. For example, if tbl_articles' record 1 matches/displays product 1 on the subform whereas record 2 matches/displays products 2,3 and 4; I need the user to be able to revisit record 1 and see the product 1 match without having to edit the text (and trigger the After Update).
I have no idea where to start with this. It's not a simple if string contains, Originally I entertained the idea of something like - Like '* [in tbl_products, record 1's products__name] *' repeated for each record (obviously not the correct syntax but simply identifying the process to myself), however this is impractical because the number of strings to match against will grow over time as more products are added.
Any help would be great,
Kind regards

Set Filter and FilterOn properties. Suggest naming subform container control different from the form it holds, like ctrProducts.
Me.ctrProducts.Form.Filter = "InStr('" & Me.articles_body & "', [products_name])>0"
Me.ctrProducts.Form.FilterOn = True

Related

linking fields between a form and a subform fails

I'm stuck with an x-file trying to do something simple that I did thousands of times but now fails in a weird way.
I have a parent form called TASKS and a subform called TASKS_LIST. They are populated with recordsets that share some fields. The two important fields here are BUNDLE_ID (long) and TASK_ID (long).
Depending on certaing condition I want to link the subform with the parent form by the field BUNDLE_ID or by the field TASK_ID.
This is how I do it ->
First, in another form, I open the TASKS form like this:
DoCmd.OpenForm "TASKS", , , mVntCrit, , , intType
Where mVntCrit filters the records in the form and intType is an int with two possible values (1,2) passed by as an openArgs.
Then, on the Form_Open event of the parent form I do this:
Dim intTypeAs Integer
intType= Nz(OpenArgs)
Select Case intType
Case 1
Me.TASKS_LIST.LinkChildFields = "BUNDLE_ID"
Me.TASKS_LIST.LinkMasterFields = "BUNDLE_ID"
Case 2
Me.TASKS_LIST.LinkChildFields = "TASK_ID"
Me.TASKS_LIST.LinkMasterFields = "TASK_ID"
End Select
When I execute the code it appears an error message on the instructions that changes the linked fields:
Runtime Error '2101'
The specified value is not valid for this property.
I've tried/checked:
Initialize first the property values with an empty string to reset
the linking fields.
Change the order of the instructions, changing the value of the LinkMasterFields property first (althoug in principle the child goes first).
Compile, compact and repair.
Checked that the fields to link are the same type and have no nulls.
Checked that the fields name are correct.
What am I doing wrong? What am I missing?
Thanks a lot.
I found the issue.
I realized that the code worked for some records and failed for other records. So I needed to know what's different between the records that work and those than do not work.
Ok, in the original form that opens de TASKS parent form, there is a list of elements from a table T1. In the TASKS and TASKS_LIST forms the table T1 joins with a table T2 by a field F.
When I open the TASKS form, I filter its records with a certain criterion being F=Some Id from T1. The thing is: there are some records where this Id exists in T1 but not in T2. Therefore, when I open and filter the TASKS form with an Id that does not exists in T2 the form return no records. If TASKS do not return any records, TASKS_LIST do not return any records either.
In that case, when TASKS_LIST return no records, if I try to meddle with properties of the form (in this case .LinkChildFields and .LinkMasterFields) an error happens. On the other hand, when TASKS_LIST return some records, I can alter the form properties without problem.
Therefore I deduce that accessing and altering some properties of forms that return no records is forbidden for some reason. It reminds me of the classic error when trying to access the value of a texbox in a form without records.

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.

How do I validate a Form input field with a Table row value in microsoft access?

I am having issues with setting a Validation rule in Access.
I have a database with the tables Clients, TypeClient, Sales, SalesList, Items.
I have a form for Sales, with a sub-form SalesList inside which has a relationship with Items in order for me to put several stock items in Sales instead of only one item.
Inside Sales table is ID, Date Sold, ID Client.
Inside SalesList is ID_List(referenced to ID in Sales), ID_Item(referenced to ID in Items), Quantity.
Inside Items is ID, Name, Stock(how much we have in stock), Price
The issue is that I am trying to validate the data I enter in the sub-form Quantity field to be higher than 0, but no higher than the available stock.
The issue is that I have tried using the expression builder to get the value from a calculated query that has 2 fields - the ID and the value for that id, with criteria that is for the ID to get it from the main form, the sub-form, the combo box input and the query works.
But when trying to get it like this:
It shows an error "The expression [Query bla] you entered in the form control's ValidationRule property contains the error The object doesn't contain the Automation object 'Query bla'".
I tried using directly the table Value(but it won't work anyway as it doesn't know for which field to get the stock value from), still the same error. I guess it can't reference to anything else? Normal validation rules work, but I want to validate it not to exceed the value of the available stock for the item I am selling now.
Nowhere on the internet there is a tutorial how to do it with expression builder. How do people validate their fields then?
It doesn't stop me in this case to sell 200 items when I currently have stock of only 2 of them, for example.
Note: I have tried DlookUp in expression builder, straight up tells me No.
Sample wrong validation rule expression builder code:
<=[Query bla]![Stock]
<=[Items]![Stock]
I am currently using VBA and fetch the record I need(the current stock) with one of the following and my unbound text is changing on every subform to the same, it shouldn't happen like that. What can I use to populate a field for each record uniquely:
Private Sub ID_Product_IZBOR_Click()
'Me.Stock_ValueField = DLookup("[Nalichnost]", "Stoka", "[ID]=" & Me.ID_Product_IZBOR)
Me.Stock_ValueField = Me.ID_Product_IZBOR.Column(2)
End Sub
Partial solution: I created a new Dim as Integer and fetched the records based on the ID provided by the field in the form and used BeforeUpdate to validate the current stock. Unfortunately the only way to see the current stock level is to click on the combo box where you choose your product and check the column for that one, it doesn't show anywhere else :(
Don't use the expression builder (I NEVER do) - just type the needed expression in property.
>0 AND <=DLookup("Stock", "Items", "ID=" & [ID_Item])
Another approach is to return Stock in textbox ControlSource then ValidationRule references that textbox. Also, user can then see the quantity limit. Methods of pulling the Stock value from Items table:
include Stock field in Items combobox RowSource - textbox then references column by its index (index begins with 0): =[cbxItems].Column(2); VBA may be needed to Requery combobox after record entry/edit is committed to table.
include Items table in form RecordSource - bind textbox to Stock field (Locked yes and TabStop no)
DLookup() expression in textbox ControlSource
Use ValidationText property to give users a custom message.
However, ValidationRule will not prevent user not entering any value if they skip the control. If you want to make sure a value is entered, either set field as required in table or use form BeforeUpdate event to validate record data.

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.

VBA for taking information from text boxes and inserting into table

So I have an input form that I want to use to update a table with certain fields of information. I have the ID of the record automatically coming up in a text box. I have three text boxes that I need to add to the table (excluding the ID) on a button click.
Name
Date
Method
are the field names.
As_Name
As_Date
As_Method
are the text box names
The table name is POC, and the ID is POC_ID (its an autonumber).
So I do not want these objects (text boxes) to be bound to the table because this is a little separate "pop-up" form that comes from a form that the table, and I only want the input to be relative to the POC_ID that is already selected via the original form.
So how do I write the vba for this to 1)check to make sure that records do not already exist....2)update the fields (listed above) with data input from the text boxes(listed above). I want to be able to use this with a button click....
EDIT:
actually it is one table not two; i have two forms that I want to be able to send information to the same table (different information though). this db was already built by someone else and know I have been deamed to take it over.
I need to add a second little pop up form for additional information to be added based on new requirements (there is literally no where for me to place this on the other one). I have already done that, and used a suggested object approach to reference the first forms (from which this second "pop-up" form springs from) to add the relative id fields. Now I have this second little pop up form that just asked three values to be inputted (which are the ones listed above).
I just simply do not know how to link the text box, with a field so that once a user enters in the information, clicks "save" it saves the information to the table relative to the TripID that is there (one mentioned above). the only way I know how to get the text boxes to save data to the table is to use the builder/wizard when I create a new one.
I would like to learn how to link an object (text box, cmb, list) etc on a form, to a table with an "On Click" method so that I can use a save button. Basically that is it!
The OpenForm method of DoCmd allows for several arguments, including Where and Openargs. You can take advantage of these.
However, something seems to be quite wrong with your table design in that you appear to be holding the same information in two tables and for no stated reason. Have you read http://www.r937.com/relational.html?
I would suggest that the design you need probably only includes a numeric field POC_ID that is a foreign key to the main table.
Still not sure I understand your situation, but let me offer the outline of an answer. If my outline is not close enough, please explain where I went astray.
Your parent form, frmParent, has a command button (cmdMoreFields) which opens the child form (frmChild) where you will enter values for 3 additional fields in the record currently displayed in frmParent. After the user enters those values in frmChild (in text box controls named As_Name, As_Date, and As_Method), she will click a command button (cmdSave) to store those values to fields (Name, Date, and Method) in table POC, and close frmChild. Also, frmParent includes a text box (txtPk_field) which holds the value for the primary key (field pk_field in table POC) of the current record.
However, I'm not sure which field/control you're using for txtPk_field, and doubt that value is available if the the current record has not yet been saved. So, I'll suggest this code for the cmdMoreFields "on click" event:
If Me.Dirty Then Me.Dirty = False
DoCmd.OpenForm "frmChild"
In the "on click" event of cmdSave (on frmChild), try code similar to:
Dim strSql As String
strSQL = "UPDATE POC SET [Name] = """ & Me.As_Name & """, [Date] =#" _
& Me.As_Date & "#, Method = """ & Me.As_Method & """ WHERE pk_field = " _
& Forms!frmParent.txtPk_field & ";"
Debug.Print strSql
CurrentDb.Execute strSql, dbFailOnError
DoCmd.Close
If that approach works, consider passing the pk_field value to frmChild with Openargs, as Remou suggested.
Note: I assumed the data type for Name is text, Date is date/time, and Method is text. You will have to change the UPDATE statement delimiters for any fields whose data types differ from my guesses.