Wednesday, October 25, 2006

Searching for Headers Using Detail Values

PeopleSoft's ability to build component search pages from search record meta-data saves developer's a lot of time. A developer can create a user friendly search page by setting a few properties on a record definition. This works great for single record and one-to-one relationship views. While a one-to-many view can be, used as a search record, there are cases where a one-to-many view will display a lot of redundant data. An example of this is searching a record that has an effective dated key. Unmodified, a search record containing the EFFDT field will display multiple rows for the same top level key. Selecting any of the effective dated rows will create the exact same level 0 buffer structure. The additional, seemingly redundant rows, displayed by this approach can confuse users.

Most of the time, we search for documents and transactions using a document or transaction header. The header usually contains the transaction ID, description, transaction date, etc. Sometimes, however, it is easier to find a transaction using a field from a detail row in the transaction. For example, a user may know a Project Activity ID, but not the Project ID. One way to provide detail row search capabilities would be to add the Activity ID to the search record. While providing the desired behavior for a user searching by Activity ID, this solution would exponentially increase the number of rows displayed in the search results to users that are not searching by Activity ID.

An alternative to using a one-to-many view for the Project Component's search record is to create a second component specifically for searching detail rows. This second component would use a one-to-many view for its search record to display both header and detail information. Based on the user's search parameters, PeopleCode can be used to transfer the user between the header (primary component) and detail (secondary component) search pages.

To implement this alternative solution, open the main component's search record and add the detail search fields. Adding the detail search fields to the search record as alternate search keys tells the component processor to add them as input fields on the advanced search page. Following the Activity ID example, add the Activity ID field to the main search record as a search key. Instead of adding the PS_PROJ_ACTIVITY record to the search view's SQL, select ' ' for the Activity ID. Save and build the view. If the user enters a value in the Activity ID search field, then, using the SearchSave event, switch components to the one-to-many view component that has a search record with the Activity ID detail values. Using the PeopleCode Transfer function, you can transfer the user to a different component while retaining all of the search key values entered by the user. From this secondary component, the user can search by Activity ID. When the user chooses a Project, the TransferExact function can be used to transfer the user back to the main component and open the selected Project using the detail component's PreBuild PeopleCode event. If the user deletes the Activity ID search criteria, then the Transfer function can be used to transfer the user back to the main search page and display the search results using the other search criteria entered by the user.

Here is what this solution would look like when applied to the PROJECT_GENERAL component.

Modify the PROJ_SRCH record as follows:

  • Add ACTIVITY_ID as a search key field below the PROJECT_ID field
  • Add , ' ' to the view's SQL between , A.PROJECT_ID and , A.DESCR.

Create a new search record that contains the one-to-many join of PS_PROJECT and PS_PROJ_ACTIVITY and name the search record NEW_PC_ACT_SRCH.

Create a new component named NEW_PC_ACT_SRCH that contains a page named UNUSED_PAGE with a search record called NEW_PC_ACT_SRCH.

Modify the Component Record PeopleCode of the PROJECT_GENERAL component by adding the following PeopleCode to the PROJ_SRCH SearchSave PeopleCode:

Local Record &keys_rec = CreateRecord(Record.NEW_PC_ACT_SRCH);
If (All(PROJ_SRCH.ACTIVITY_ID)) Then
&keys_rec.BUSINESS_UNIT.Value = PROJ_SRCH.BUSINESS_UNIT.Value;
&keys_rec.PROJECT_ID.Value = PROJ_SRCH.PROJECT_ID.Value;
&keys_rec.ACTIVITY_ID.Value = PROJ_SRCH.ACTIVITY_ID.Value; &keys_rec.DESCR.Value = PROJ_SRCH.DESCR.Value;
Transfer( False, MenuName.COMPONENT_MENU, BarName.CUSTOM, ItemName.NEW_PC_ACT_SRCH, Page.UNUSED_PAGE, "U", &keys_rec, True);
End-If;

In the NEW_PC_ACT_SRCH component, add the following PeopleCode to the NEW_PC_ACT_SRCH search record SearchSave event:

Local Record &keys_rec = CreateRecord(Record.NEW_PC_ACT_SRCH);

If (None(NEW_PC_ACT_SRCH.ACTIVITY_ID)) Then
&keys_rec.BUSINESS_UNIT.Value = NEW_PC_ACT_SRCH.BUSINESS_UNIT.Value;
&keys_rec.PROJECT_ID.Value = NEW_PC_ACT_SRCH.PROJECT_ID.Value;
&keys_rec.DESCR.Value = NEW_PC_ACT_SRCH.DESCR.Value;
Transfer( False, MenuName.CREATE_PROJECTS, BarName.USE, ItemName.PROJECT_GENERAL, Page.PROJECT_GEN_01A, "U", &keys_rec, True);
End-If;

In the NEW_PC_ACT_SRCH component, add the following PeopleCode to the component's PreBuild event:

Local Record &keys_rec = CreateRecord(Record.PROJ_SRCH);

&keys_rec.BUSINESS_UNIT.Value = NEW_PC_ACT_SRCH.BUSINESS_UNIT.Value;&keys_rec.PROJECT_ID.Value = NEW_PC_ACT_SRCH.PROJECT_ID.Value;
Transfer( False, MenuName.CREATE_PROJECTS, BarName.USE, ItemName.PROJECT_GENERAL, Page.PROJECT_GEN_01A, "U", &keys_rec, True);

Leveraging PeopleSoft's JavaScript Save Warning

In a standard PeopleSoft session, a user will enter data into components. If a user forgets to save a component before navigating to a new component or opening a different transaction, then that user will loose all of the changes he or she made to the current transaction. To mitigate the potential for loosing changes, PeopleSoft displays a save warning message to remind the user to save. I am sure that you have encountered this message while working in PeopleSoft. I am also sure that you have noticed that PeopleSoft only displays this message if you change something within a component before navigating away from that component.

As you add custom JavaScript and HTML to your PeopleSoft implementation, you may find yourself in a situation where you need to know if a user has modified a component. For example, if you add an HTML Area to a component and that HTML Area contains links to related transactions, then, when a user navigates to a different transaction using these custom links, that user may loose any changes he or she made to the current transaction.

To warn a user before taking action using JavaScript, you can use the following PeopleSoft defined JavaScript functions:

  • saveWarning(frameName,form,target,url)
  • checkFrameChanged(objFrame)
  • checkAnyFrameChanged(startingFrame)
  • checkFormChanged(form, objFrame)

The saveWarning function will check for changes in a frame or form. If anything in the form or frame has changed, then the function will display a confirm message asking the user if he or she wants to save before leaving. Depending on the user's answer, the function will either navigate to the new url or cancel the action. If the user chooses to leave the component, then the function will return true.

saveWarning parameters:

  • frameName: the name of a frame to test
  • form: an object reference to a form to test
  • target: the target frame name for the url
  • url: the URL to load into the target frame

If you want to display a warning message for changes in any frame or form then you can substitute '' for the frame name and null for the form object. For example, the statement saveWarning('',null,'_self','') will cause the function to display a warning message if anything has changed in any frame or form.

If you just want to test for changes, and don't want to display a message, then you can use one of the check*Changed() functions. For example, to check for changes on any frame or any form, then you can use the statement checkAnyFrameChanged(top.frames). Here is an example of using the checkAnyFrameChanged function:

if(checkAnyFrameChanged(top.frames)) {
// something changed
} else {
// nothing changed
}

If you use one of the check*Changed functions to display a custom message, then be sure to consider internationalization.

The check*Changed functions have similar parameters to the saveWarning function. This is because the saveWarning function calls several of the check*Changed functions using the parameters passed to the saveWarning function.