If you are reading this, you are probably familiar with creating fields, records, pages, components, and menus. With these objects alone, you can create working development projects. Simple ones, yes, but these objects are all you need for the most basic level of development. PeopleCode comes into play for those development projects requiring a little more control. For example, you know how to define the default of a field within the record definition. This means that a value is always placed in this field whenever it is empty. But what if you needed to have a default placed in a field based on a condition of another field? You cannot do this within the record definition, but you can by using PeopleCode. The PeopleCode capability enables you to set and control a lot of parameters and objects. To fully understand the PeopleCode system and develop with it, you first need to learn about how it works.
PeopleCode is a visual language similar to Microsoft Access or Visual Basic. In fact, in PeopleTools version 8.1, the coding syntax has been changed to reflect the Visual Basic (called VB from here on) standard. This makes the PeopleCode language easier to use and learn for developers who already know VB or the Visual Basic for Applications (VBA) programming language.
The first detail to learn about PeopleCode is not the language itself but how the code works within the framework of PeopleSoft. PeopleCode can be placed in events on the field definition in each record. PeopleCode can also, since PeopleSoft 8.1, be placed into events on fields within pages and components. Each event is a place of action that launches PeopleCode when certain processes take place on a page.
Using events is a difficult concept to grasp at first and you might have to spend extra time to fully understand this process.
PeopleSoft comes with 17 events for PeopleCode. Each event has a specific function and control for which it is designed.
To best understand the PeopleSoft Panel Processor, consider the process of starting and running a page to see when all the types of events are going to run.
First, the user selects from the menu to run a specific page in a certain action (this action being Add, Update/Display, Update/Display All, or Correction). The Panel Processor then looks at the menu selection to find the component connected with it. The component contains the search record for the action in which the user has asked the system to run. The search record keys, alternate keys, and list information are gathered from the search record that has been assigned to the component. The search page is created online with the keys and alternate keys displayed for entry, but before the user can enter any data, the first event of PeopleCode activates.
After this code is complete, the user is able to interact with the search page to search records and select the one they wish to process. Upon clicking OK in this search panel, the next event becomes active.
If no error messages are encountered within the code, the main page, as requested from the menu by the user, is now opened. Data, as selected from the search page, is then ready to be loaded into the page. Before the data is actually loaded, each row of data that is to be loaded onto the page is validated by the code contained in the next event.
The page is now filled with data from the search page, but the processor is not yet ready for user input. A series of events must launch for each row of data already loaded into the page.
Each one of these events runs, in order, on all rows of data within the page. After all these events run, and if no errors have occurred, the page is open for entry by the user, finally. But this is only the beginning of what PeopleCode can do. Now, depending on the action of the user, more PeopleCode may activate.
If the user changes a field (modifies or deletes) or adds data to an empty field, the following PeopleCode events run. Of course, the standard PeopleSoft record edits must pass first, such as the prompt values as defined in the record definition for this field.
If the user inserts a new row of data on a page (only for occurs levels 1 through 3), then the following PeopleCode events run for all the fields on the record that is within the level where the add was performed. To better explain, if the insert was done on level 2, then only the PeopleCode events on the record definitions contained within level 2 are activated. The PeopleCode events within the record definition on levels 0 or 1 are not activated.
If the user deletes a row of data on a level 1 through 3 scroll, then the following PeopleCode events run for the row deleted and then for all the rows that are left within the level where the delete occurred.
The last step the user can take is to save the page. The save process runs another set of PeopleCode events, as listed below.
As you can see, the PeopleCode events activate based on certain actions requested by the user, such as starting a page, changing data, or inserting new rows.
You will also note that some events of PeopleCode are running a lot of the time. You need to understand the flow of the events so that when you have a function or code to add to the system, you place the code in the correct event and therefore it runs at the appropriate time to perform the correct action. You can place code in an incorrect event so that it runs too much—this will affect your system by slowing it down.
This section teaches you about each event, providing examples of use including actual PeopleCode. Developers already familiar with PeopleCode will see the same events but there are some new things to learn as well - these are fully discussed in the "New to Version 8.1" section below.
The code in the SearchInit event is needed only for records that will be used as search records. If the record definition you have is not used as a search record, then code placed in here will never run.
Code placed in this event is typically used to set up default values on the search page. For example, this event is mostly used to set up counters or defaults from the operator preferences on the search panel. For example:
SetSearchEdit(BUSINESS_UNIT);
SetSearchDefault(BUSINESS_UNIT);
This example turns on edits and system defaults for the Business Unit field in the search page. This code does not cause any other PeopleCode category to activate; it simply sets up the information in the page object to be used in that specific field.
This code also applies to the search page and is typically used to validate the search page to make sure all the required fields are filled in. Because you cannot specify the required fields on a search record (the record definition's required attribute is for use only on the regular pages), you can place code here to do the validation of all the fields that must be filled in. For example:
If None(BUSINESS_UNIT) Then
Error MsgGet(9000, 3, "Business Unit is a required field.");
End-If;
check_status(BUSINESS_UNIT, PO_ID, &STATUS);
If &STATUS = "C" Or &STATUS = "X" Then
Error MsgGet(10200, 164, "The specified PO exists, and is either Completed or Cancelled.");
End-If;
check_auto_num(BUSINESS_UNIT, &AUTO_NUM);
If None(&AUTO_NUM) Then
Error MsgGet(10000, 1, "%1 is not a Purchasing business unit.", BUSINESS_UNIT);
End-If;
If &AUTO_NUM = "N" And PO_ID = "NEXT" Then
Error MsgGet(10200, 34, "This business unit is not set up for purchase order auto numbering.");
End-If;
This example shows a little more about the capability of this event of code. The code here, although complex for now basically checks that certain fields are filled in. If they are filled in, then the data is validated for specific conditions. If an error condition exists, an error message is displayed and the search page is not closed.
Now you have arrived at events that are used more often in development projects. The code in the RowInit event runs every time a new row of data loads into a page. This section activates code for each level and each data row. For example, if you have a panel with a level 1 that has 15 data rows that load, the RowInit event will run 16 times: once for the level 0 (header information) and then 15 times for each row of data on level 1.
You need to understand that even though on the page you can see only five rows of data, this code activates for all rows of data that are loaded into the buffer. So it is not how many rows are visible on the page, but how many rows are loaded into the data buffer that determines how many times this PeopleCode category activates. Consider the following 3 examples:
If %PanelGroup = PANELGROUP.VCHR_PO_STD Then
If All(PO_ID) Then
VCHR_PANELS_WRK.TXN_CURRENCY_SRC = "P";
End-If;
End-If;
If INSTALLATION.PO = "Y" Then
DERIVED.EDITTABLE13 = "ITM_PURCH_FS";
End-If;
Gray(LINE_NBR);
Gray(SCHED_NBR);
Gray(BUSINESS_UNIT_RECV);
These examples show different ways to use the RowInit event that are typically done. The first example shows, based on some information, that you can set some values into special fields. The second example shows that you can also set or change the prompts used. Finally, the third example shows how you can override the settings on the page to hide, gray (display-only), unhide, or ungray (not display-only) fields based on the initialization.
In this event, field defaults can be coded. This event works only to set defaults into fields where the logic is too complex to use the record definition. If, by contrast, you have a hard default that is always to be used, then you should set that value in the record definition. Otherwise, you can set the default based on a condition check of some other field or value using code from the FieldDefault event. For example:
This example checks whether the ITM_SETID field is filled in. If the field is not filled in, then the code runs a function to get the information and place it in this field.
The FieldChange event occurs under two circumstances: once when a field is changed, and the other when a button is pressed.
For the field change event, the code does not execute unless there is a net change (changing the value of a field to it's original value is not a net change) and the user moves out of the field or saves the page. Here you can do very complex validation upon the field.
This example checks the values in a couple of fields and then, based on their settings, runs a different function. In the FieldChange event, you are not limited to using only the field from which the code is launched, but rather, you can access any field on the page as is shown in this example.
You can also use the FieldChange event when you have a button on a panel; here you would place the code that you wish to activate when the button is pressed. The code you place for a button can be just about anything you want. This is what makes button actions so powerful—they can perform all sorts of actions, controls, and verifications. When you have a button and it is pressed, you are usually looking for some action to take place: jump to another panel, fill in the current panel with data based on some criteria you have set, or run a process, for example.
UnhideScroll(RECORD.PO_LINE);
UnGray(GOTO_LINE_DTLS);
SEL_LINE_FLG = "Y";
LINES_SAVED = "N";
&WHERE = "where BUSINESS_UNIT = :1 and PO_ID = :2 and LINE_NBR >= :3";
If All(LINE_NBR_TO) Then
&WHERE = &WHERE | " and LINE_NBR <= :4";
End-If;
ScrollFlush(RECORD.PO_LINE);
ScrollSelect(1, RECORD.PO_LINE, RECORD.PO_LINE, &WHERE, PO_HDR.BUSINESS_UNIT, PO_HDR.PO_ID, LINE_NBR_FROM, LINE_NBR_TO, True);
This example is from a button which, when pressed, is going to fill in data based on some criteria. The scroll, on level 1 in the panel, is un-hidden (now visible on the panel), and the data buffer is emptied for this scroll (scroll flush). Finally, the scroll's data buffer is filled with data based on a selection criterion.
In the FieldEdit event, you apply most of the field editing to validate information within a field. You already can do one simple validation using the prompts on a field as defined within the record definition, but this event gives you the ability to validate using multiple fields and conditions. This is also the event to apply messages from PeopleCode that will not crash the panel, to warn or stop the process within the page.
The FieldEdit event is where you should place validation that you want to occur within the page field entry process. If you have validations for which you do not want to leave the field before a specific criteria or process is complete, then you need to place code in FieldEdit. In this event, the action is based only on the field you are in, so be careful not to validate against another field that you cannot change. If you do validate against another field, you will be locked in a loop and you won't be able to leave this field.
To better explain this potential loop problem, assume that you have two fields on a panel. The first field can be set to a value of Red or Blue only. The second field can be set to a number that represents the brightness of the color (that is, the first field). Now red can have a brightness only between 1 and 5, whereas blue can have a brightness between 3 and 9. So you place FieldEdit code in the first field (the color field) that states that if the value is blue, then the value of the second field must be between 1 and 5. This meets the logic requirement, but if you understand the FieldEdit process, you will see how this will get you into trouble. The user enters Red into the first field and tabs out to the next field. The PeopleSoft Panel Processor then runs the FieldEdit code for the field that states that if the value is red (which is what the user entered), then the value of the next field must be between 1 and 5. Because the user has not entered a value in the next field, the value is initialized as 0. Therefore, this FieldEdit code fails and requires the user to input a value that will not fail. Because you've allowed only red and blue, and neither is allowed to have a 0 value, then the user can never leave this field. This is a simple explanation, but it shows how you have to be careful when using FieldEdit code for validation using other fields. You have to understand the capabilities of the system and how the rules will be applied. In this example, the best solution is to change the location where the validation code is placed. The validation code should be placed in the Field Edit event of the brightness field, or it could be moved to the SaveEdit event (about which you have not learned yet).
The way that PeopleSoft will pass or fail a validation is by issuing a message. If you fail the validation, a message must be called. If you pass the validation, then no messages are displayed. There are two types of messages: warning and error. Each is a message, but they carry different levels of actions.
A warning message tells the user that they might not want to continue or to use this value in the field. With a warning message, the user can decide to stop and fix the issue or move on and ignore the message. A warning means that the value in this field is not normal, but the user is still allowed to continue. An error message , on the other hand, does not offer the user a choice; the user must stop and fix the problem. You should make sure that all decisions to error or warn are well documented in the technical documentation for your modification.
The following example shows how to use the FieldEdit to do complex validation with an error message. If a problem occurs, issue an error message to the user stating the problem and do not allow the process to continue until it is resolved.
check_item();
If None(PO_HDR.VENDOR_ID) Then
Error MsgGet(10200, 76, "Vendor %1 is not set up for item %2.", PO_HDR.VENDOR_ID, INV_ITEM_ID);
End-if;
This event is typically reserved for custom-built functions. You have already learned about the flow of how PeopleCode activates from the PeopleSoft Panel Processor and that the process FieldFormula runs for all sorts of actions. You should not place any code in this category except custom-built functions.
Consider the following example:
Function update_sched() &CHANGED_SCHED = "N";
UpdateValue(LINE_NBR, &LINE, PO_LINE_SHIP.UNIT_PRC_TOL, &SCHED, &UNIT_PRC_TOL);
UpdateValue(LINE_NBR, &LINE, PO_LINE_SHIP.PCT_UNIT_PRC_TOL, &SCHED, &PCT_UNIT_PRC_TOL); End-Function;
This example shows a function that performs some actions. A function can take in multiple values and even send back values to the calling program. This enables you to write business logic into functions that many programs can call so that you do not have to write the logic over and over in multiple PeopleCode categories. This is important to PeopleCode 8.1 in that you now have the ability to place code in new places. By having more places—pages and components—in which to load PeopleCode, you need to remember that you may require the same code in multiple places or categories. Instead of copying the code into all these different places and events, you can create the one function and then call it from the other places, thereby having your logic in only one place. Due to this added complexity, you will need to think more in terms of functions when writing code. Writing more functions that you place into this event will save you time and effort in the long run.
This event is used to filter data loaded into a scroll level. This is an advanced technique that will not be covered in this book. Most developers who want to restrict rows of data create a view to use on the page with the restriction in the SQL where clause. By using this event, you can apply this restriction as you need to or you can even change the criteria as the page is being created.
This event is used to activate code when a delete is performed for a row of data rather than for only a single field. This delete can be on any level. The developer, when using this code even, can control whether a row of data can be deleted by checking for orphaned records or other required validations. The purpose of this code is to prevent deletes unless a specific criterion has been met.
The process to show the pass or fail of the event is just like in the FieldEdit event. If you failed the validation, pass a message. If you do not issue any messages, then the delete process has passed the validation. This category can also use the warning and error message commands: An error always fails; a warning reports an issue to the user.
The code here checks a field for a specific status. If a certain status is set, then the code prevents the deletion by issuing an error message. If the status is not set, the code will allow the deletion by not issuing any error or warning message.
In the RowInsert event, you apply code to inserts of new data rows. Data, when loaded into the page, is validated through the RowInit event, but when you add a new record (level 0) or a new row of information (level 1 through 3), there is no RowInit to activate because it was not initiated into the panel. The RowInsert can and should perform the up-front validations that you would have placed in RowInit for new data rows being added.
|
Note |
As you can see, you might need to place the same code in the RowInit and RowInsert categories. This is another reason for looking at code that you could place into functions so that you do not end up copying the same code into multiple categories. |
If VCHR_PANELS_WRK.FULL_SET_FLG = "Y" Then
MERCHANDISE_AMT = VCHR_PANELS_WRK.VCHR_BALANCE_AMT; VCHR_PANELS_WRK.LN_BALANCE_AMT = MERCHANDISE_AMT;
End-If;
This code checks a work record field for a specific status and then updates some fields using information in some other work record fields. The process done here can be just about anything you think of, from graying (display only) and hiding fields to doing complex logic for recalculating a total.
In the SaveEdit event, you place code that validates on a save. This is used for the final validation of the record. Usually, you can do a lot of this validation in the FieldEdit event, but you might want the user to do all the input they can and then do a check just prior to the save. You might also have to validate multiple fields as a unit. It is hard to do this within FieldEdit, so you can place the overall validation in this event instead.
A final use for the SaveEdit event occurs when the user does not tab through every field so the FieldEdit code never activates. If you still need this validation, you will have to add it to SaveEdit to be sure that the code does run.
As with the other edit events, you can prevent or just warn on a save when the validation is incorrect. If you prevent, then the user is unable to save the panel. If you warn, then the user can stop the save process and edit the problem or they can continue. The process is the same regardless of whether you're using warning or error messages. Of course, no message means that you have passed the SaveEdit validation.
If DISTRIB_MTHD_FLG = "Q" And None(QTY_VCHR) Then
Error MsgGet(7030, 49, "You must enter either a quantity on Voucher Line %1 when distributing by quantity. Or you can delete the line.", VOUCHER_LINE_NUM);
Else
If None(MERCHANDISE_AMT) Then
Error MsgGet(7030, 49, "You must enter either a merchandise amount on Voucher Line %1 when distributing by amount. Or you can delete the line.", VOUCHER_LINE_NUM);
End-If;
End-If
This code checks some values in the fields and then, if certain conditions exist, an error message is issued, preventing the save of this panel.
In the SavePreChange event, you can place code to change or update the data tables prior to the save being committed to the database. This is your last chance to make changes. In your page, you are making changes, but these changes are loaded only into your panel buffers. It is not until you save the record that the database is updated. Once the save is started, the PeopleCode runs and does all its SaveEdit checks and then just prior to saving the information to the database, this event occurs. This event enables you to do other processes and update other rows of data, assuming that the process will complete.
The SavePreChange event, is used mostly to update hidden fields that need to be changed or corrected based on user input just prior to saving. You do not do updates within SaveEdit, as this is only supposed to be looking for errors. If you need to total a field in level 1 and place the information in a header record (level 0), here is where you would make the code work, since you do not want to update the header level field with every row insert or field change on the level 1. This enables you to put code in one place, and it runs doing the work just prior to the save.
Many developers will place SQLexec function calls in this category. SQLexec function calls enable you to run a SQL statement directly so that you can update a related field that is not within the page. You have to be aware that using SQL direct statements can lead you into trouble. You must be sure that the record you are going to update or insert is not any- where within the component. If any field of the record is on the component, then you will receive an error: This panel has been updated by another user . This error message is telling you that something happened to the data between the time you retrieved the data and the time you saved it. Someone else grabbed the information and changed it prior to your change committing it to the database. The problem occurred because of your SQLexec—it ran and made an update to the database. The PeopleSoft Panel Processor then tries to commit the rest of your changes and sees that someone else (yes, even though the SQLexec came from your process, the processor sees this as another user) has made a change, so your panel is not allowed to save. So be extra careful when using SQL calls within the events.
For &BUFFER_ROW = 1 To &CURRENT_ROW
UpdateValue(VOUCHER_ID, CurrentRowNumber(), DISTRIB_LINE.DESCR, &BUFFER_ROW, DESCR);
End-For;
This code loops through all the rows of a level 1 panel and updates a value in a lower-level scroll (level 2).
In the SavePostChange event, you add code that you want to activate after the rows of data have been saved to the database. Code placed here could be line totals and other calculated fields. This is also where a lot of SQL execs are written and performed to update other necessary fields.
If %Mode = "A" and VENDOR.VENDOR_PERSISTENCE = "O" Then
&SETID = GetSetId("BUSINESS_UNIT", BUSINESS_UNIT, "VENDOR", "");
SQLExec("Update ps_vendor set VENDOR_STATUS = 'I' where setid = :1 and vendor_id = :2", &SETID, VENDOR_ID)
End-If;
This code example shows that if the page is in Add mode and the vendor is set to a special persistence, then the vendor status must be set back to inactive so that no further POs can be used. This process is used on vendors that are set to one time use only. After you use them on a PO , the status is set to inactive so that you do not use them again. You can see in this example how you are updating the vendor based on an action in the PO system.
The workflow event is for the specific use of workflow. You need to put in specific code here that updates the workflow records and processes. Workflow is beyond the scope of this book.
With the release of PeopleTools 8.1, PeopleSoft has made some major changes to the PeopleCode system. You are now able to place code not only within the record definition, but you can also place code in the page, component, Application Engine, and Business Components. This means that you have more places to enter code to meet your development needs. These new capabilities are explained in this section.
|
Note |
Application Engine and Business Components are specialized, advanced tools that are not covered in this book. They are mentioned here so that you know about all the new places to locate PeopleCode, but no further mention will be made. |
The process of PeopleCode running on the system has already been mentioned, but what happens when PeopleCode is encountered within the new areas such as pages and components? Well, the same overall order still applies. For example, when a scroll level has data loaded into it, the RowInit code runs for each row of data. Now you are only adding a new level of complexity to RowInit: first, all the RowInit code runs from the record definition, then all the page code RowInit activates, and finally the RowInit code in the component area runs. So the overall flow of the code running through the events stays the same, but now you have some new areas to run within each event.
Let's explore each new section of PeopleCode within the page and component, reviewing why they would be used and what events are available.
Why would you place code in a page versus a record definition? If you have code for a RowInit-type function that you wish to have run only when you are accessing a specific page, then you should place code in that page. This way, you do not need to write code to retrieve the page name and then check whether you need to run this code.
This requirement to have code run when within a specific page is used all the time, because if you include one field from a record, all the PeopleCode of the record loads into the page and runs. You might have a page that is usually used for this record and you hide this field until a specific action is done. Now, when you are in the other panel, the code will run, but this field does not exist on the page because it is for a whole different process. You would have had to retrieve a value for the page you are in to then see whether you wish to run the code.
Now with the new capability in PeopleSoft 8.1, you can place code directly into the page so that it will activate only when the page is accessed.
The event here is a new name called Activate. Although it is a new name, you can swap it for RowInit. This is the only event available for the page; the code entered here is associated with the page itself.
Because you could place code only in the Activate event within the panel, there was still a need to place code so that it runs only when a specific page is running. PeopleSoft tools developers created a new place to load PeopleCode within the component to solve this problem. You now have access to many more events that run only when in a specific component.
Within the component definition, you have access to the following events:
• SearchInit
• SearchSave
• RowInit
• RowSelect
• RowInsert
• RowDelete
• SaveEdit
• SavePreChange
• SavePostChange
• FieldChange
• FieldDefault
• PreBuild
• PostBuild
• SavePreChange
• SavePostChange
• Workflow
• FieldEdit
As you can see from the list, you have all the events—including two new ones: PreBuild and PostBuild. The order of the events and how they activate is not changed within the component. Having this new capability enables you to enter code into the component so that the code will run only when this component is active.
Remembering the rule requiring all the PeopleCode of a record to be loaded, even when using only one field on a page, let's see how you can save time and effort by using this new capability. Instead of having to write code to check what component you are in for every piece of PeopleCode you have in the record definition, you can place just the right amount of code within the component PeopleCode events. This makes the PeopleCode easier to write and much more efficient.
Usually, you enter all the PeopleCode into the record and field definition area, but now you can enter the code into the component. Using the component PeopleCode events, it gets a little more complex. You can enter code against the component itself, fields within the component and, finally, records within the component.
The PreBuild event is specific to the component; this code runs prior to the component being built. This is typically used to hide, unhide, gray, or ungray fields, or to set certain values in the component.
PeopleCode within the component record gives you an opportunity to set variables that can be used later by PeopleCode located in other events. PreBuild can also be used to validate data in the search page just as is done in the SearchSave event. This event runs right after the RowSelect event within the flow of PeopleCode.
The PostBuild event is specific to the component; this code runs after the component is built. This is usually used to hide, unhide, gray, or ungray fields, or to set certain values in the component. The event can be thought of just like the RowInit event as the data is already loaded into the component. This even runs right after the RowInit even in the PeopleCode flow