Peoplesoft SETID and Business Unit

Business Units
Business Units are logical units created within any organization for reporting purposes and don’t have any predetermined restrictions or requirements. This flexible structuring device enables you to actually define a higher level of reporting for your employees and group them according to functional or administrative tasks or for your business purposes.

One can define Business Units that reflect the specific functional needs of your internal human resources departments, or reflect the actual business structure of your enterprise. Your Business Units may be, for example, companies, agencies, subsidiaries, divisions, departments, or branch offices within your organization. Or, you may choose to have a single Business Unit represent your entire organization. It’s up to you and your unique business needs.

TableSets and SetIDs
Once you’ve established Business Units, you can define TableSets, or groups of tables, for your system-wide control tables, so that you can share the same code values among multiple Business Units within your enterprise. Conversely, the TableSet feature also enables you to limit access to specific data to only those users who need it, while maintaining all of your data on the same tables. The flexibility to share TableSets among Business Units enables you to centralize redundant information while you keep other information, such as Department and Job Codes, decentralized. You can use Business Units and TableSets to associate a Business Unit with employees in your enterprise and to specify how default values for currencies and country codes will behave throughout the HRMS system, based on either the user’s Permission List or the Business Unit that the system is referencing during a particular business process or activity.

Where a Business Unit organizes your company or your organization, SetIDs help you organize your data within the system. The HRMS system uses tables (Control Tables or Prompt Tables) that use a high-level key that enables you to identify and retrieve data from the system. A secondary high-level key, referred to as a SetID, has also been added on various tables. SetIDs are simply the labels used to identify a TableSet. Business Unit and SetID functionality in PeopleSoft also provides you with a higher business level for reporting purposes and other business data roll-up.

Peoplecode setting value to Null

Peoplecode setting value to Null

String

Always use space to assign a null value to a string.
e.g. &Test_Str = ” “;

Number

Use 0 (zero) to assign a null value to a number.
e.g. &Test_Num = 0;

Date

Always Use blank value to assign a null value to a date otherwise it will give you invalid date error if it is equated to null or space as string.
e.g. &Test_Date = “”;

Other Data Types

For other data types like record, rowset we can assign them to null so as to get accurate results from all() and none() functions by paasing them as parameter.
e.g. &Test_Rec = null;

Application Class Properties

We can not use all() or none() functions to check if any property of application class is set. For this we will need to check by equating it to null or blank or space or zero based on its type in a conditional statement.

Peoplecode App Class reference

http://peoplesoftlearnings.blogspot.my/2008/03/application-classes-in-peoplesoft.html

http://peoplesoftlearnings.blogspot.my/2010/03/instance-variable-in-peoplecode.html

http://jjmpsj.blogspot.my/2014/07/private-app-class-members.html

 

https://docs.oracle.com/cd/E41509_01/pt852pbh2/eng/psbooks/tpcd/chapter.htm?File=tpcd/htm/tpcd04.htm

 

Application Classes in Peoplesoft – Reference sheet / Points remember

 

  • Importing packages or classes

<Pacakge name>:<Subpacakge name>:<…>:<Class name or wild card>

 

  • Class Extensions represents the “is-a” relationship.

When a class extends another class, it’s called a subclass of that class.

 

  • No Multiple inheritances or Concept of interface in Application classes.

 

  • Instance variables in the class are accessible in all the objects of that class.

 

  • Application programs generally pass parameters by value, which is not the same as for existing functions.

 

  • Parameter passing with object data types is by reference.

 

  • When the objects are passed to method and it reassign the object with new object it will not reflected in the calling method.

 

  • Application Programs use the out specifier to pass a parameter by reference.

Method increment (&Value as number out); rem passed by reference.

 

  • Create is the key word used to create the class object.

Local MyObjectClass &o1 = Create MyobjectClass (‘A’);

Local MyObjectClass &o1 = Create Test: Examples: MyobjectClass (‘A’);

 

  • If parameter in a method is having out specifier then parameter should be a variable and cannot be constant.

 

  • A class that extends another class must have constructor, and in the constructor it must initialize its super class.

To initialize a superobject, an instance of the superclass is assigned to the keyword %Super in the constructor for the subclass.

 

Class Example extends ExampleBase

Method Example ();

End-class;

 

Method Example

%Super = create ExampleBase ();

&BaseStrin = &currentBaseString;

&Slashstring = &BaseString;

End-Method;

 

  • Before the constructor runs, the instance variables for the class are set by default takes on their declared types.

 

  • An application class doesn’t have destructors, that is, a method called just before an object is destroyed. The People code runtime environment generally cleans up any held resources.

 

  • When application class properties and instance variables are used as the argument to functions or methods, they are always passed by value, never by reference.

 

  • %This is can be used to self reference. This is to refer the same object.

 

  • %Super = %This – this should never be done it will go to an infinite loop.

 

  • Import Fruit:* – imports all the classes in the application package.

 

  • Import statements are validated when you save the Peoplecode.

 

  • Peoplesoft recommends that you use read-write or ready only properties instead of creating methods name GetXxx and SetXxx.

 

  • Getter and Setter syntax

Get Propertyname

End-get;

 

  • External Functions Declarations are allowed in application classes, in the global and component variable declarations, after the class declaration (after the end-class) and before the method definition.

 

  • %Super is only required to access superclass members that are hidden by overriding members in the current time.

 

  • Downcasting is the process of determining if an object is of a particular subclass type. If the object has that subtype (either the object is of that subtype, or is a subtype of it), a reference to the subject is returned, otherwise Null is returned. In either case, the type of the resulting value is compatible with the name subclass type.

 

  • Class Fruit

Property number fruitcount;

End-class;

 

Class Banana extends Fruit

Property number BananaCount;

End-Class;

 

Local Banana &MyBanana = Create Banana ();

Local Fruit &MyFruit = &MyBanana;   /* Okay, Banana is a subtype of Fruit */

Local number &num = & MyBanana.BananaCount;

 

&MyBanana = &MyFruit as Banana;   /* &MyFruit is currently a Banana at runtime */

&Num = (&MyFruit as Banana).BananaCount; /* same as &MyBanana.BananaCount */

&MyFruit = Create Fruit ();

&MyBanana = &MyFruit as Banana; /* Assigns Null – &Myfruit isn’t a Banana */

 

  • /* */ and /**  */ comments are allowed. Comments enclosed in  /** — */ are potentially be used to generate API documentation.

 

  • Method header comments are uses some tags which helps in API documentation

Some of the tags are

  • @Parameter N
  • @exception name
  • @return type

Class header comments contains tags

  • @version X (value of version)
  • @author  name

 

Example

 

/**

* Class header comments

* @Version 1.0

* @author Ganesh

*/

 

Import PWWPack: ExampleBase

 

Class Example extends ExampleBase

Method Example ();                                     /*Constructor*/

Method NumToStr (&Num As number) Returns string ();

Method AppendSlash ();

Property number SlashCount get;                   /*Get properties */

Property number ImportantDayofWeek get set; /*Get and set properties */

Property string Slashstring readonly;   /* values can be assigned in constructor */

Property date ImporantDate;

Private

Method NextDayofWeek (&Dow As number) returns date;

Constant &Sunday = 1;              /*Constants */

Instance string &Basestring;      /* Instance variables are like static variable */

End-class;

 

Declare function getusername Peoplecode FUNCLIB_PP.STRING_FUNCTIONS FieldFormula;

 

/**

* Method header comments example

* @param Dow is a number parameter

* @exception it throws Invalid day exception if the number is negative.

* @return   it is returns the date as of that week.

*/

Method NextDayofWeek  (&Dow)

———

———

End-method;

 

/* get block */

 

Get SlashCount

Return (Slashcount);

End-get;

 

 

– Posted by Ganesh

It might be confusing for many of them, how is instance variables different from the Private and Public variables.

Myth :- specific object (instance of a class) would only have access to its own instance variables.

Instance variable is very different to Private or Public. Infact it is not right comparison of Instance with Private and Public.

Instance variables equal to Static variables in the other Object oriented programing language (like C++ and Java).

Instance – Defines the scope of the variable where as private and public key words define access levels/Visiblity of the variables. If variable is not defined as Instance then SCOPE of the variable is Local by defult (Local to the created object of the class).

Instance – scope of the variable is across the objects of the same class and scope of the Instance variable is at the class level.

The variables are created when the first object of the class is creted and will be present till last object of the same class is present in the memory.

If the Instance variable is declared as Private it is accessed the way it is accessed i,e only with in the class methods. if variable is declared as Public it is accessed the way you access other public variables. (i,e both within the class and outside of the class).

Simple comparison that can be done..(just for understanding purpose).

Component variables has scope within the component similarly instance variables has scope at the class level across the objects of the same class.

Big question arise when do we need these kind of variables.

1) if you want to count how many objects of the class was created you increment this variable in the constructor of the class.
2) If you have constant value that needs to shared across the objects of the class then you can use the instance variables.
3) Shared variable across the objects like component variable (across the different events of the component).

 

“Private” App Class Members

I was reading Lee Greffin’s post More Fun with Application Packages — Instances and stumbled across this quote from PeopleBooks:

A private instance variable is private to the class, not just to the object instance. For example, consider a linked-list class where one instance needs to update the pointer in another instance.

What exactly does that mean? I did some testing to try and figure it out. Here is what I came up with:

  1. It is still an instance variable which means each in-memory object created from the App Class blue print has its own memory placeholder for each instance member.
  2. Instances of other classes can’t interact with private instance members.
  3. Instances of the exact same class CAN interact with private members of a different instance.
  4. Private instance members differ from static members in other languages because they don’t all share the same pointer (pointer, reference, whatever).

I thought it was worth proving so here is my sample. It is based on the example suggested in PeopleBooks:

For example, consider a linked-list class where one instance needs to update the pointer in another instance.

The linked list is just an item with a pointer to the next item (forward only). A program using it keeps a pointer to the “head” and then calls next() to iterate over the list. It is a very common pattern so I will forgo further explanation. Here is a quick implementation (in the App Package JJM_COLLECTIONS):

class ListItem
   method ListItem(&data As any);
   method linkTo(&item As JJM_COLLECTIONS:ListItem);
   method next() Returns JJM_COLLECTIONS:ListItem;
   method getData() Returns any;
private
   instance JJM_COLLECTIONS:ListItem &nextItem_;
   instance any &data_;
end-class;

method ListItem
   /+ &data as Any +/
   %This.data_ = &data;
end-method;

method linkTo
   /+ &item as JJM_COLLECTIONS:ListItem +/
   &item.nextItem_ = %This;
end-method;

method next
   /+ Returns JJM_COLLECTIONS:ListItem +/
   Return %This.nextItem_;
end-method;

method getData
   /+ Returns Any +/
   Return %This.data_;
end-method;

Notice the linkTo method sets the value of the private instance member of a remote instance (its parameter), NOT the local instance. This is what is meant by private to the class, not private to the instance. Each instance has its own &nextItem_ instance member and other instances of the exact same class can manipulate it. Here is the test case I used to test the remote manipulation implementation:

import TTS_UNITTEST:TestBase;
import JJM_COLLECTIONS:ListItem;

class TestListItem extends TTS_UNITTEST:TestBase
   method TestListItem();
   method Run();
end-class;

method TestListItem
   %Super = create TTS_UNITTEST:TestBase("TestListItem");
end-method;

method Run
   /+ Extends/implements TTS_UNITTEST:TestBase.Run +/
   Local JJM_COLLECTIONS:ListItem &item1 =
      create JJM_COLLECTIONS:ListItem("Item 1");
   Local JJM_COLLECTIONS:ListItem &item2 =
      create JJM_COLLECTIONS:ListItem("Item 2");
   
   &item2.linkTo(&item1);
   
   %This.AssertStringsEqual(&item1.next().getData(), "Item 2",
      "The next item is not Item 2");
   %This.Msg(&item1.next().getData());
end-method;

The way it is written requires you to create the second item and then call the second item’s linkTo method to associate it with the head (or previous) element.

Now, just because you CAN manipulate a private instance member from a remote instance doesn’t mean you SHOULD. Doing so seems to violate encapsulation. You could accomplish the same thing by reversing the linkTo method. What if we flipped this around so you created the second item, but called the first item’s linkTo? It is really the first item we want to manipulate in a forward only list (now, if it were a multi-direction list perhaps we would want to manipulate the &prevItem_ member?). Here is what the linkTo method would look like:

method linkTo
   /+ &item as JJM_COLLECTIONS:ListItem +/
   %This.nextItem_ = &item;
end-method;

Now what if we wanted a forward AND reverse linked list? Here is where maybe the ability to manipulate siblings starts to seem a little more reasonable (I still think there is a better way, but humor me):

class ListItem
   method ListItem(&data As any);
   method linkTo(&item As JJM_COLLECTIONS:ListItem);
   method next() Returns JJM_COLLECTIONS:ListItem;
   method prev() Returns JJM_COLLECTIONS:ListItem;
   method remove() Returns JJM_COLLECTIONS:ListItem;
   method getData() Returns any;
private
   instance JJM_COLLECTIONS:ListItem &nextItem_;
   instance JJM_COLLECTIONS:ListItem &prevItem_;
   instance any &data_;
end-class;

method ListItem
   /+ &data as Any +/
   %This.data_ = &data;
end-method;

method linkTo
   /+ &item as JJM_COLLECTIONS:ListItem +/
   REM ** manipulate previous sibling;
   &item.nextItem_ = %This;
   %This.prevItem_ = &item;
end-method;

method next
   /+ Returns JJM_COLLECTIONS:ListItem +/
   Return %This.nextItem_;
end-method;

method prev
   /+ Returns JJM_COLLECTIONS:ListItem +/
   Return %This.prevItem_;
end-method;

method remove
   /+ Returns JJM_COLLECTIONS:ListItem +/
   %This.nextItem_.linkTo(%This.prevItem_);
   REM ** Or manipulate both siblings;
   REM %This.prevItem_.nextItem_ = %This.nextItem_;
   REM %This.nextItem_.prevItem_ = %This.prevItem_;
   Return %This.prevItem_;
end-method;

method getData
   /+ Returns Any +/
   Return %This.data_;
end-method;

And here is the final test case

import TTS_UNITTEST:TestBase;
import JJM_COLLECTIONS:ListItem;

class TestListItem extends TTS_UNITTEST:TestBase
   method TestListItem();
   method Run();
end-class;

method TestListItem
   %Super = create TTS_UNITTEST:TestBase("TestListItem");
end-method;

method Run
   /+ Extends/implements TTS_UNITTEST:TestBase.Run +/
   Local JJM_COLLECTIONS:ListItem &item1 =
      create JJM_COLLECTIONS:ListItem("Item 1");
   Local JJM_COLLECTIONS:ListItem &item2 =
      create JJM_COLLECTIONS:ListItem("Item 2");
   Local JJM_COLLECTIONS:ListItem &item3 =
      create JJM_COLLECTIONS:ListItem("Item 3");
   
   &item2.linkTo(&item1);
   
   %This.AssertStringsEqual(&item1.next().getData(), "Item 2",
      "Test 1 failed. The next item is not Item 2");
   %This.AssertStringsEqual(&item2.prev().getData(), "Item 1",
      "Test 2 failed. The prev item is not Item 1");
   
   &item3.linkTo(&item2);
   %This.AssertStringsEqual(&item1.next().next().getData(), "Item 3",
      "Test 3 failed. The next.next item is not Item 3");
   %This.AssertStringsEqual(&item1.next().next().prev().getData(), "Item 2",
      "Test 4 failed. The prev item is not Item 2");
   
   Local JJM_COLLECTIONS:ListItem &temp = &item2.remove();
   %This.AssertStringsEqual(&item1.next().getData(), "Item 3",
      "Test 5 failed. The next item is not Item 3");
   %This.AssertStringsEqual(&item1.next().prev().getData(), "Item 1",
      "Test 6 failed. The prev item is not Item 1");
   
end-method;

I hope that helps clear up some of the confusion around the term “private” as it relates to Application Classes.

Peoplesoft: Get the version of PeopleTools

Find it in file of

peopletools.properties

 

D:\ps_home\peopletools.properties

 

#Peopletools Install Settings
#Thu Jun 27 17:28:04 UTC 2013
installlocation=D\:\\ps_home
dbcodessuffix=PT
unicodedb=0
licensegroupname=PeopleTools
hstplt=unknown
psplatformregname=MICROSFT
psserver=App;Batch;File;Web
psdbbin=c\:\\Program Files\\Microsoft SQL Server\\100\\Tools\\Binn
dbtypedescr=Microsoft SQL Server
psenv=App;Batch;File;Web
dbtype=MSS
tuxedodir=
licensegroup=06
psplatform=Windows
productversion=8.53.04

Peoplesoft Template Based Hire extension

From this: http://bloggingaboutoracleapplications.org/extending_template_based_hire_tbh_custom_fieldchange_prompt_events/index.html

 

Extending SmartHire (Template Based Hire) with custom Field Change & Field Prompt Events

 

‘Hiring new employees or adding contingent workers into the system is one of the most time-consuming tasks for Human Resources departments. Template-Based Hire reduces the current labor-intensive data entry process through the Personal Data and Job Data pages by providing a configurable, template-driven approach. Template Administrators can define default data for various sections or fields in the hiring process and they can determine what sections should be displayed, hidden and made  equired to the end-user. This allows organizations to deploy policy control and flexibility in multiple template configurations for the end-users. This flexibility provides Human Resources departments the ability to decentralize the hiring process out to line managers or HR representatives in the field, rather than only allowing centralized hiring.’

Source: Red Paper Template Based Hire Red Paper for Human Resources 9.0.

With the SmartHire you can configure templates with common fields that are required for hiring an employee/contingent worker. On these templates you configure (predelivered) sections with fields, corresponding the PERSON, JOB etc record fields. These fields contain hardcoded validation, where available. Fortunately Oracle has also created a user exit, which you can use to build and configure your own business process validations and prompts. Unfortunately this not been documented. When you look at the screenshot below, you have two sections, where you can configure your own FieldChange event and Field Prompt event.

But what are the prerequisites for the Event class? Which methods need to be used? Which parameters need to be used? Does it need to have return values?

First thing you start to do, is start looking for documentation. There is a red paper available on Oracle Support  document 747744.1, Template Based Hire Red Paper for Human Resources 9.0. This states:

The Field Change App Class provides a user exit for field change code to be added. Some delivered section fields include field change code. For example, the Company field on the delivered Work Location – Job Fields section has field change code that builds prompt lists for other fields in the section, such as Location and Establishment ID. It is recommended that any validation methods added at implementation be stored in customer-created application classes.
The Field Prompt App Class section provides a field prompt user exit. Normally the prompt values come from a prompt table, but a program can be written instead to supply these values. We did not ship any field prompt code, but customers can add their own, if needed. We may actually deliver some field prompt code as system data in future as part of fixes for performance issues.

When you look at PeopleBooks for this subject, it states:

The Field Change App Class provides a user exit for field change code to be added. Some delivered section fields include field change code. For example, the Company field on the Work Location – Job Fields section has field change code that builds prompt lists for other fields in the section, such as Location and Establishment ID.

Note. Oracle recommends that any validation methods added to the PeopleSoft application at implementation be stored in a customer-created application class.

So I had to dig into the code, to see what was needed to build a custom Event class. After a couple of hours, I stumbled upon the Package HRTMPL. This package contains all classes to build the UI of SmartHire and set all FieldChange and Field Prompt Events. These (hardcoded) FieldChange and Field Prompt events get set by the class FieldChangeFactory and FieldPromptFactory.  Let’s take a closer look at the FieldChangeFactory class, here is a portion of the class.

import HRTMPL:TMPL:hrTmplEvent;
import HRTMPL:FC:*;

class FieldChangeFactory extends HRTMPL:TMPL:hrTmplEvent
   method FieldChangeFactory();
   method execute();
end-class;

method FieldChangeFactory
   %Super = create HRTMPL:TMPL:hrTmplEvent();
end-method;

method execute
   /+ Extends/implements HRTMPL:TMPL:hrTmplEvent.execute +/

   Local string &FieldName, &RecordName;
   Local object &lObjEvent;
   Local HRTMPL:FC:FieldChange &objFieldChange;

   If %This.getSectionField().isFieldEvent() Then
      &lObjEvent = CreateObject(%This.getSectionField()
      .getEventClassPath() | ":" | %This.getSectionField()
      .getEventClassID());
      ObjectDoMethod(&lObjEvent, "setTmplRowSet", %This.getTmplRowSet());
      ObjectDoMethod(&lObjEvent, "setSectionField", %This.getSectionField());
      ObjectDoMethod(&lObjEvent, %This.getSectionField().getEventMethod());

   Else

      &FieldName = %This.getSectionField().getName();
      &RecordName = %This.getSectionField().getParentRecord().getName();

      Evaluate &RecordName
      When Record.JOB

         Evaluate &FieldName
         When Field.BUSINESS_UNIT
            &objFieldChange = create HRTMPL:FC:JobBusinessUnit();
            Break;
         When Field.REG_REGION
            &objFieldChange = create HRTMPL:FC:JobRegRegion();
            Break;
         Evaluate [OTHERFIELDS, ETC, ETC]
         When-Other
         End-Evaluate;
         Break;

      When-Other;
      End-Evaluate;

      If All(&objFieldChange) Then
         &objFieldChange.setTmplRowSet(%This.getTmplRowSet());
         &objFieldChange.setSectionField(%This.getSectionField());
         &objFieldChange.execute();
      End-If;
   End-If;
end-method;

When you look at this code, you’ll see that if a custom FieldChange is set on a field, the code runs the following lines

1 &lObjEvent = CreateObject(%This.getSectionField().getEventClassPath() |
  ":" | %This.getSectionField().getEventClassID());
2 ObjectDoMethod(&lObjEvent, "setTmplRowSet", %This.getTmplRowSet());
3 ObjectDoMethod(&lObjEvent, "setSectionField", %This.getSectionField());
4 ObjectDoMethod(&lObjEvent, %This.getSectionField().getEventMethod());

On the first line the Object is created that you defined in the FieldChange section of the field configuration. After this, the method setTmplRowSet is executed and after this the method setSectionField is executed. After this your custom method is called,  you defined in the FieldChange section of the field configuration. You might think you need a custom class with three methods.

When you look at the predelivered  FieldChange classes, you can create the following class hierarchy diagram.

You can see the predelivered  FieldChange classes (PersonBirthCountry, PersonNIDCountry, etc) get extended from FieldChange class, which in it’s turn get extended from the hrTmplEvent class. The hrTmplEvent class already contains the setTmplRowSet  and setSectionField methods.

So, if you were to create your custom class, you should extend the HRTMPL:FC:FieldChange class. You would have to create only one custom method, since the other two methods are inherited from the superclass.

Now that we know what the class needs to look like and where it should reside, let’s create a custom package and class for the field Position Number. Whenever you change the Position Number, I would like to default the Salary Admin Plan, Grade and Step from the PositionData table. The following example will execute this.

 

import HRTMPL:FC:FieldChange;
class hrTbhFieldChangePosition extends HRTMPL:FC:FieldChange
   method hrTbhFieldChangePosition();
   method execute();
end-class;
/* Constructor */
method hrTbhFieldChangePosition
   %Super = create HRTMPL:FC:FieldChange();
end-method;

method execute
   /+ Extends/implements HRTMPL:FC:FieldChange.execute +/
   Local string &PosNbr;
   Local Record &rPositionData;
   Local string &SalAdminPlan, &Grade, &Step;

   /* Get selected position number */
   &PosNbr = %This.getTmplRowSet().getField(Record.JOB, 1, Field.POSITION_NBR)
   .getValue();

   /* Check if position has a value, onload this in empty */
   If All(&PosNbr) Then

      /* Get Position defaults from POSITION_DATA record */
      &rPositionData = CreateRecord(Record.POSITION_DATA);
      &rPositionData.POSITION_NBR.Value = &PosNbr;
      &rPositionData.SelectByKeyEffDt(%Date);

      /* Get default values from Position Data */
      &SalAdminPlan = &rPositionData.SAL_ADMIN_PLAN.Value;
      &Grade = &rPositionData.GRADE.Value;
      &Step = &rPositionData.STEP.Value;

      MessageBox(0, "", 0, 0, "Choosen position: " | &PosNbr |
      "; Set SalAdminPlan/Grade/Step to: " | String(&SalAdminPlan) |
      " / " | String(&Grade) | " / " | String(&Step));

      /* Set Field Values from Position Data */
      %This.getTmplRowSet().getField(Record.JOB, 1, Field.SAL_ADMIN_PLAN)
      .setValue(&SalAdimPlan);
      %This.getTmplRowSet().getField(Record.JOB, 1, Field.GRADE)
      .setValue(&Grade);
      %This.getTmplRowSet().getField(Record.JOB, 1, Field.STEP)
      .setValue(&Step);
   End-If;
end-method;

The same way the Field Prompt event is constructed.

When you look at the FieldPromptFactory class, you will see the following lines:

If %This.getField().isPromptEvent() Then
   &lObjEvent = CreateObject(%This.getField().getPromptClassPath() |
   ":" | %This.getField().getPromptClassID());
   ObjectDoMethod(&lObjEvent, "setTmplRowSet", %This.getTmplRowSet());
   ObjectDoMethod(&lObjEvent, "setField", %This.getField());
   ObjectDoMethod(&lObjEvent, %This.getField().getPromptMethod());

This class creates your custom FieldPrompt class, executes method setTmplRowSet and setField and then executes your custom method.

The following class diagram illustrates the hierarchy.

So, if you were to create your custom class, you should extend the HRTMPL:FP:FieldPrompt class. You would have to create only one custom method, since the other two methods (setField, setTmplRowSet) are inherited from the superclass. Let’s say we want the Position Number to have only three positions eg 19000017, 19000018, 19000019. The custom class would look like this:

import HRTMPL:FP:FieldPrompt;
import HRTMPL:TMPL:hrTmplField;
import HRTMPL:TMPL:hrTmplCodeList;
import HRTMPL:TMPL:hrTmplCode;
class hrTbhPromptPosition extends HRTMPL:FP:FieldPrompt
   method hrTbhPromptPosition();
   method execute();

   property HRTMPL:TMPL:hrTmplField objTmplField;
end-class;
/* Contructor */
method hrTbhPromptPosition
   %Super = create HRTMPL:FP:FieldPrompt();
end-method;

method execute
   /+ Extends/implements HRTMPL:FP:FieldPrompt.execute +/
   Local integer &i;
   Local HRTMPL:TMPL:hrTmplCodeList &lobjCodeList;
   Local HRTMPL:TMPL:hrTmplCode &lobjCode;
   Local Rowset &rsPositionData;
   Local string &PositionNbr, &PositionDescr;

   /* Get current field executing this method */
   &objTmplField = %This.getField();

   /* Create custom rowset for dropdown values */
   &rsPositionData = CreateRowset(Record.POSITION_DATA);
   &rsPositionData.Fill("WHERE POSITION_NBR IN
   ('19000017','19000018','19000019')");

   /* Create runtime list of values object */
   &lobjCodeList = create HRTMPL:TMPL:hrTmplCodeList();

   /* Loop through custom rowset positions */
   For &i = 1 To &rsPositionData.ActiveRowCount
      /* Get Position Number & Description */
      &PositionNbr = &rsPositionData(&i).GetRecord(1)
      .POSITION_NBR.Value;
      &PositionDescr = &rsPositionData(&i).GetRecord(1)
      .DESCR.Value;

      /* Create runtime value object */
      &lobjCode = create HRTMPL:TMPL:hrTmplCode(&PositionNbr,
      &PositionDescr);

      /* Add runtime value object to list of values object */
      &lobjCodeList.addCode(&lobjCode);

   End-For;

   /* Set runtime list of values object to current Field */
   &objTmplField.setCodeList(&lobjCodeList);

end-method;

I have created these two classes in a custom package CUSTOM_HRTMPL.

These classes are configured on the Position Number field, as seen in the second screenshot of this post.

Now let’s see it in action on a PeopleSoft Vanilla Demo environment using Template KUEMP_MGR_POSITION, with custom events on section WORK_LOC_POS_NBR on field Position Number.

FieldPrompt with only the position numbers 19000017, 19000018, 19000019

Finally the FieldChange event when selecting a position number