Multiple Value Lookups
What is a multiple value lookup?
A multiple value lookup is a special form of the normal lookups. In normal lookups, depending on how the lookup was invoked (i.e. from the portal or a document), the user typically has the option of returning a record to the calling page or to copy/edit that record. In multiple value lookups, each result row is displayed with a check box next to it. When the user clicks on the "return selected" button, all of the checked records will be returned to the calling page.
Multiple value lookups for maintenance documents
The maintenance framework provides a significant amount of support when returning from multiple value lookups. Normally, no java code needs to be written to return values.
maintainableCollection
definitions need to have several attributes defined that will provide the MV lookups with the information necessary to perform all processing.
<bean parent="MaintainableCollectionDefinition"> <property name="name" value="accountGlobalDetails"/> <property name="businessObjectClass" value="org.kuali.kfs.coa.businessobject.AccountGlobalDetail"/> <property name="sourceClassName" value="org.kuali.kfs.coa.businessobject.Account"/> .... <property name="maintainableFields"> <list> <bean parent="MaintainableFieldDefinition" p:name="chartOfAccountsCode" p:required="true" p:readOnlyAfterAdd="true" p:template="chartOfAccountsCode"/> <bean parent="MaintainableFieldDefinition" p:name="accountNumber" p:required="true" p:readOnlyAfterAdd="true" p:template="accountNumber" p:webUILeaveFieldFunction="loadAccountName"/> <bean parent="MaintainableFieldDefinition" p:name="account.accountName" p:unconditionallyReadOnly="true" p:template="accountName"/> <bean parent="MaintainableFieldDefinition" p:name="newCollectionRecord"/> </list> </property> </bean>
<maintainableCollection>
's need to have the following attribute defined:
sourceClassName
: the type of BOs that will be queried. It is not always the same type of BO which are elements in the maintainable collection. In the example, we are maintaining a collection ofAccountGlobalDetail
BOs, and when we perform a MV lookup, we are using the values in theAccount
BO (the source BO) to fill in values in theAccountGlobalDetail
BO.Removed Attribute
As of KFS release, the
sourceAttributeName
attribute was removed from the maintainable collection definition. To upgrade from a prior reason to release, just remove the attribute from the XML tag.<maintainableField>
's may have one attribute defined to enable multiple value lookups:template
: designates the name of the attribute from which the value of the source BO will be copied into the maintained collection element. In the example,AccountGlobalDetail
'schartOfAccountsCode
will be copied fromAccount
'schartOfAccountsCode
,accountNumber
is copied fromaccountNumber
, andaccount.accountName
is copied fromaccountName
. Note that not all field definitions need to have a template; these fields are simply left blank upon returning from a multiple value lookup.
Multiple value lookups for transactional/other documents
This type of documents is less structured. Therefore, more steps are required to return values from a multiple value lookup.
Rendering a multiple value lookup icon
Rendering a lookup tag is simple: simply invoke the multipleValueLookup.tag from the JSP page.
The following are the MV lookup specific attributes for this tag:
- boClassName (required): the class name of the business object being looked up
- lookedUpBODisplayName (not required): this value is the human readable name of the BO being looked up (e.g. Object Code)
- lookedUpCollectionName (required): the name of the collection being looked up (perhaps on a document collection), this value will be returned to the calling document.
Struts form support
To return from a MV lookup, forms for the return struts action must/should support the retrieval of the following parameters:
- refreshCaller (should support): a value of "multipleValues" indicates that the MV lookups were used
- lookedUpCollectionName (should support): the name of the collection being looked up. It should be the property name of the collection, relative to the document object.
- lookupResultsSequenceNumber (must support): if not blank, this represents the serial number of the lookup. If blank, may represent that the user cancelled lookup, or returned with no value
- lookupResultsBOClassName (must support): represents the class that was being looked up. This is not necessarily the class of the collection elements. This corresponds to the "boClassName" value passed into the multipleValueLookup.tag tag.
If the latter two variables are non-blank, then it signifies that the user attempted to return results
Nervous system support
- After retrieving the lookupResultsSequenceNumber and the lookupResultsBOClassName and they are non-blank, find the class corresponding to lookupResultsBOClassName (using
Class.forName
) - Get the lookupResultsService Spring bean, and invoke something similar to the following
KNSServiceLocator.getLookupResultsService().retrieveSelectedResultBOs(lookupResultsSequenceNumber, lookupResultsBOClass, GlobalVariables.getUserSession().getUniversalUser().getPersonUniversalIdentifier());
This method returns a java.util.Collection of the BOs selected.
Passing the universal user id ensures that only the user that initiated the lookup may retrieve those results
Add returned result BOs into a document collection
The section above described how to retrieve a collection of the BOs being looked up. Often, in many documents that invoke multiple value lookups, the selected rows are returned as some additional elements in some (sub)collection defined on the document. The class type of the looked up BO does not match with the collection element type. For example, for the account global documents, a multiple value lookup is available to lookup multiple Account
objects, but the collection to which these results are added has elements of type AccountGlobalDetail
.
The KNS provides support to facilitate the addition of rows into the collection.
// retrieve the list of BOs Collection<PersistableBusinessObject> rawValues = KNSServiceLocator.getLookupResultsService() .retrieveSelectedResultBOs(lookupResultsSequenceNumber, lookupResultsBOClass, GlobalVariables.getUserSession().getUniversalUser().getPersonUniversalIdentifier()); // request is the http servlet request String lookedUpCollectionName = request.getParameter(RiceConstants.LOOKED_UP_COLLECTION_NAME); // the collection from the document, and we're assuming that it's of type java.util.Collection Collection documentCollection = (Collection) ObjectUtils.getPropertyValue(document, lookedUpCollectionName); // the type of elements from the documentCollection variable above Class collectionElementClass = ...; if (rawValues != null) { // see below for an explanation of this Map Map<String, String> template = getTemplate(); for (PersistableBusinessObject nextBo : rawValues) { PersistableBusinessObject templatedBo = (PersistableBusinessObject) ObjectUtils.createHybridBusinessObject(collectionClass, nextBo, template); // Do other necessary processing before adding to the collection, including setting fields untouched by ObjectUtils.createHybridBusinessObject documentCollection.add(templatedBo); } }
In the above example, the template
map is used to indicate how fields from the looked up BO (e.g. Account
) are mapped into the collection element BO (e.g. AccountChangeDetail
). For each key-value mapping in the map, the key is a field name in the collection BO, and the corresponding value is a field name in the looked up BO.
In the method call ObjectUtils.createHybridBusinessObject(collectionClass, nextBo, template)
above:
- A new BO of type
collectionClass
is constructed. - For each key-value mapping of
template
, the field named with the key of the new instance created in (1) is assigned with the value of the field innextBo
named with the mapping's value. - The instance created in (1) is returned.
To see how the maint doc framework implements adding elements to a collection, look at KualiMaintenanceDocumentAction.refresh(ActionMapping, ActionForm, HttpServletRequest, HttpServletResponse)