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

Laravel: Creating DB Table definition (make:migration)

C:\wamp\www\blog2>php artisan make:migration create_meetings_table –create=meetings
Created Migration: 2017_03_26_062354_create_meetings_table

C:\wamp\www\blog2>

It will create the following file:

C:\wamp\www\blog2\database\migrations\2017_03_26_062354_create_meetings_table.php

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateMeetingsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create(‘meetings’, function (Blueprint $table) {
$table->increments(‘id’);
$table->timestamps();
});
}

/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists(‘meetings’);
}
}

 

 

 
C:\wamp\www>cd blog2

C:\wamp\www\blog2>php artisan make:migration create_mtgdelegates_table -create=mtgdelegates
[Symfony\Component\Console\Exception\RuntimeException]
Too many arguments, expected arguments “command” “name”.

C:\wamp\www\blog2>php artisan make:migration create_mtgdelegates_table -create=mtgdelegates

C:\wamp\www\blog2>php artisan make:migration create_mtgdelegates_table -create=mtgdelegates
[Symfony\Component\Console\Exception\RuntimeException]
The “-c” option does not exist.

C:\wamp\www\blog2>php artisan make:migration create_mtgdelegates_table –create=mtgdelegates
Created Migration: 2017_03_30_095555_create_mtgdelegates_table

C:\wamp\www\blog2>composer dump-autoload
Generating autoload files

C:\wamp\www\blog2>composer dumpautoload
Generating autoload files

C:\wamp\www\blog2>

FizzBuzz Java

My attempt at the program, took me a bit because i am not used to Java syntax

 

package fizzbuzz;

/**
* @author MM
*/
public class FizzBuzz {

/**
* @param args the command line arguments
*/
public static final int NUM_THREE = 3;
public static final int NUM_FIVE = 5;

public static void main(String[] args) {
// Fizz Buzz
// Write a program that prints the numbers from 1 to 100. But for multiples of three print “Fizz” instead of the number and
//for the multiples of five print “Buzz”. For numbers which are multiples of both three and five print “FizzBuzz”.”

for (int i = 1 ; i <= 100; i++) {

if (( i%NUM_THREE == 0) && ( i % NUM_FIVE == 0)) {
System.out.println(“FizzBuzz”);
}
else if ( i%NUM_THREE == 0) {
System.out.println(“Fizz”);
} else if ( i%NUM_FIVE == 0) {
System.out.println(“Buzz”);
} else {
System.out.println(i);
}
}
}
}

JAVA HeadFirst MiniMusic Code

Here is the full code for the program. In addition to that, you need to make sure you have the soundbank in your JRE folder. Google JAVA Soundbank to download the file. And then extract the file in the following folder

C:\Program Files\Java\jre1.8.0_121\lib\audio (OR)

C:\Program Files\Java\jre<VERSION>\lib\audio

C:\Users\MM\Google Drive\PROGRAMMING\Java\MiniMusicCmdLine\src\minimusiccmdline>javac MiniMusicCmdLine.java

C:\Users\MM\Google Drive\PROGRAMMING\Java\MiniMusicCmdLine\src\minimusiccmdline>cd ..

C:\Users\MM\Google Drive\PROGRAMMING\Java\MiniMusicCmdLine\src>java minimusiccmdline.MiniMusicCmdLine 102 33

THE PROGRAM:

/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
PAGE 346, Chapter 11 (PDF Page 379)
*/
package minimusiccmdline;

import javax.sound.midi.*;

/**
*
* @author mustnap
*/
public class MiniMusicCmdLine {

/**
* @param args the command line arguments
*/
public static void main(String[] args) {
MiniMusicCmdLine mini = new MiniMusicCmdLine();
if (args.length < 2) {
System.out.println(“Don’t forget the instrument and note args”);
} else {
int instrument = Integer.parseInt(args[0]);
int note = Integer.parseInt(args[1]);
mini.play(instrument, note);
}

} // close main

public void play(int instrument, int note) {

try {
Sequencer player = MidiSystem.getSequencer();
player.open();
Sequence seq = new Sequence(Sequence.PPQ, 4);
Track track = seq.createTrack();
MidiEvent event = null;

ShortMessage first = new ShortMessage();
first.setMessage(192, 1, instrument, 0);
MidiEvent changeInstrument = new MidiEvent(first, 1);
track.add(changeInstrument);
ShortMessage a = new ShortMessage();
a.setMessage(144, 1, note, 100);
MidiEvent noteOn = new MidiEvent(a, 1);
track.add(noteOn);
ShortMessage b = new ShortMessage();
b.setMessage(128, 1, note, 100);
MidiEvent noteOff = new MidiEvent(b, 16);
track.add(noteOff);
player.setSequence(seq);
player.start();

Thread.sleep(2000);
player.close();
System.exit(0);

} catch (Exception ex) {
ex.printStackTrace();
}

} // close playe

} //close class

Laravel (005) Database Migration/Creation

I encountered an issue with my DB migration

C:\wamp\www\blog2>php artisan migrate:refresh

[ErrorException]
Undefined index: 2017_02_18_144317_create_tasks_table

C:\wamp\www\blog2>php artisan migrate:refresh
Migration table not found.
Migration table created successfully.
Migrated: 2017_02_18_152427_create_users_table

C:\wamp\www\blog2>php artisan migrate:refresh
Rolled back: 2017_02_18_152427_create_users_table
Migrated: 2017_02_18_152427_create_users_table

 

The issue was due to the Table MIGRATIONS containing non existent table definition (that got created/migrated before) so the system encountered an error when re-creating the table. Dropping the table MIGRATIONS an running the refresh command should resolve the issue

 

Laravel Journey (004) PHP Tinker

This is useful for testing commands to get data from DB.

However, TINKER is accessed via a Laravel site (installation). The logic is this, TINKER will allow people to query the Database, and the DB MUST BE LINKED to SOMETHING, in this case, the site  we are querying.

So it must be executed from the folder of the site.

 

C:\Windows\system32>php artisan tinker
Could not open input file: artisan

C:\Windows\system32>cd \

C:\>cd wamp\www\blog2

C:\wamp\www\blog2>php artisan tinker
Psy Shell v0.8.1 (PHP 5.6.25 ΓÇö cli) by Justin Hileman
New version is available (current: v0.8.1, latest: v0.8.2)
>>>

Laravel Journey (003) Installation (OpenSSL issue)

Copy the folder created by the commandline

 

You might encounter this issue

-and this is due to the config on PHP.ini file.

there are 2 PHP.ini file.
1. Wamp

C:\wamp\bin\php\php7.0.10

2. Apache.

C:\wamp\bin\apache\apache2.4.23\bin

(in windows this Apache file is a linked filed but you can edit it from here – open in notepad or similar and edit it).

There is this line in the file that is commented out, which you need to remove the comment , so that the config will be used

extension=php_openssl.dll

Laravel Journey (002) Installation

Once Laravel is installed, you can run the following command via CMD line

laravel new blog

(replace ‘blog’ with whatever the site name you want)

Be aware of the location/path you run the command from as it will be the path the files are copied to.

If you are using WAMP, the www path is on the following directory (by default)

C:\wamp\www

Laravel Journey (001) Installation

  1. Download COMPOSER  https://getcomposer.org/download/
  2. Install COMPOSER.
  3. Install WAMP
  4. The following PATH will should be there

    C:\wamp\bin\php\php5.6.25;

    C:\ProgramData\ComposerSetup\bin;

    C:\Users\MM\AppData\Roaming\Composer\vendor\bin 

  5. Through COMPOSER, install LARAVEL