Lucky Dip Logo
Powered by PlusNet. PlusNet broadband.
Patrick's Lucky Dip
Home > eBusiness > Loading Assignments

Patrick Haston
30 August 2007

Loading Assignments into Oracle HR

The most difficult part for us was loading assignment data. As we were loading into a shared instance many of the people already had default assignments. We needed to end date these assignments if they started before a certain date (the 1st April 2007) and then set them to be correct from then on.

We created a table to hold the data called UPLOAD_GB_EMP_ASG. You can see the script here.

We then created a procedure called XX_UPDATE_PER_ASSIGNMENT. You can see the full script here but I'll take you through the key parts.

begin

-- loop for each person in the update_gb_emp_asg table
-- there could be many entries in this table (for staff who have
-- had career changes).
 
for person in (select distinct person_id
    from my_apps.update_gb_emp_asg where DATA_TRANSFERRED='N')
loop

As you can see in the code above we start by loop through each individual in the data to be loaded. We only want to look at the records which have not yet been transferred.

-- first task is to make sure that the data already in the system is
-- correct.  An issue is those assignments that start before the
-- 1 April.  We need to end date these.

-- find out the start date of this person's most recent assignment
begin

  select max(effective_start_date)
  into lv_effective_start_date
  from hr.per_all_assignments_f 
  where person_id = person.person_id;
  -- Each person will only have one assignment at a time as we have been using
  -- a shared instance of Oracle HR.

exception
  when no_data_found then
     lv_effective_start_date := null;
end;

We look up the PER_ALL_ASSIGNMENTS_F table to find the start date of their most recent assignment. There should only be one assignment for each person in the system, but we check just in case.


-- if this person should have been given an assignment in a previous
-- stage - if they don't have an assingment by now, it's too late to
-- fix it.
if lv_effective_start_date is null then
  UPDATE my_apps.update_gb_emp_asg
  SET DATA_TRANSFERRED ='F',
     error_message = 'No assignment found in PER_ALL_ASSIGNMENTS_F.'
  WHERE person_id= person.person_id;
  COMMIT;

Check to see if they have an assignment. If not, we record the fact in the upload table and that's the end for this person.


else
  -- we are good to proceed.
  if lv_effective_start_date < to_date('01-APR-2007') then
    -- we need to end-date this assignemnt.
    -- we do this by updating their most recent assignment
    -- when we call the first API.
    lv_datetrack_update_mode := 'UPDATE';
  else
    -- We can safely edit the existing record 
    lv_datetrack_update_mode := 'CORRECTION'; 
  end if;

Now we check to see if this assignment record was created before the start date for our HR data. If so, then we need to use the UPDATE setting in the API to create a new record. If it was created after the start date then we can simply edit the existing record using the CORRECTION parameter setting.


  -- Initialise this variable.  This is used to track how many career changes
  -- a person has had since 1 April 2007.  Each career change will need
  -- to start with an update.
  lv_career_entry := 0;
 
  -- Get the career record from 1 April onwards
  FOR career IN (SELECT *
                 FROM my_apps.update_gb_emp_asg
                 where person_id = person.person_id  
                   and data_transfered='N'
                 order by effective_start_date ) 
  LOOP 
       
    -- increment the career entry number 
    lv_career_entry := lv_career_entry + 1; 
         
    -- test to see if this is another career record for this person 
    if lv_career_entry > 1 then 
      -- if so, we need to update the record to create a new row 
      -- in the assignments table. 
      lv_datetrack_update_mode := 'UPDATE'; 
    end if; 

The LV_CAREER_ENTRY variable is used to track the number of career changes a person has had. All career entries after the first have to be UPDATEs to the data. The career cursor simply loops through all entries in the source data table for the current person in date order to give their career changes to date.


    -- Get the object version number for the assignment record we 
    -- are going to update or correct. 
    -- In every case this should be the most recent entry. 
    select MAX(AA.OBJECT_VERSION_NUMBER) 
    into lv_object_version_number 
    from HR.PER_ALL_ASSIGNMENTS_F AA 
    WHERE AA.person_id = person.person_id; 
                
    -- set variables 
    lv_special_ceiling_step_id := null; 
    lv_soft_coding_keyflex_id := null; 
    lv_group_name := null; 
    lv_org_now_no_manager_warning := null; 
    lv_other_manager_warning := null; 
    lv_spp_delete_warning := null; 
    lv_entries_changed_warning := null; 
    lv_concatenated_segments := null; 
    lv_gsp_post_process_warning := null; 
    lv_tax_district_c_warning :=null; 

We set the values for the various variables. The most important one is the object_version_number, as if we get this wrong the change will be rejected.

    -- now we are ready to call the first API
    -- we need to call two APIs because each one only does some of the
    -- attributes that we need.
    begin
      -- we call the UPDATE_GB_EMP_ASG first
      hr_assignment_api.update_gb_emp_asg
      (  p_validate                     => false
        ,p_effective_date               => career.effective_start_date
        ,p_datetrack_update_mode        => lv_datetrack_update_mode
        ,p_assignment_id                => career.assignment_id
        ,p_object_version_number        => lv_object_version_number
        ,p_supervisor_id                => career.supervisor_id
        ,p_assignment_number            => TRIM(career.assignment_number)
        ,p_change_reason                => TRIM(career.change_reason)
        ,p_frequency                    => career.frequency
        ,p_manager_flag                 => career.manager_flag
        ,p_normal_hours                 => career.normal_hours
        ,p_perf_review_period           => career.perf_review_period
        ,p_perf_review_period_frequency => career.perf_review_period_frequency
        ,p_probation_period             => 6
        ,p_probation_unit               => 'M'  
        ,p_sal_review_period            => 1
        ,p_sal_review_period_frequency  => 'Y'
        ,p_time_normal_finish           => career.time_normal_finish
        ,p_time_normal_start            => career.time_normal_start
        ,p_ass_attribute1               => TO_CHAR(to_date(career.ass_attribute1),
                                             'YYYY/MM/DD HH24:MI:SS' )
        ,p_ass_attribute3               => TRIM(career.ass_attribute3)
        ,p_ass_attribute4               => TO_CHAR(career.effective_end_date,
                                             'YYYY/MM/DD HH24:MI:SS' )
        ,p_employee_category            => lv_employee_category
        ,p_supervisor_assignment_id     => career.supervisor_assignment_id
        ,p_cagr_grade_def_id            => lv_cagr_grade_def_id
        ,p_cagr_concatenated_segments   => lv_cagr_concatenated_segments
        ,p_concatenated_segments        => lv_concatenated_segments
        ,p_soft_coding_keyflex_id       => lv_soft_coding_keyflex_id 
        ,p_comment_id                   => lv_comment_id
        ,p_effective_start_date         => lv_effective_start_date
        ,p_effective_end_date           => lv_effective_end_date
        ,p_no_managers_warning          => lv_org_now_no_manager_warning
        ,p_other_manager_warning        => lv_other_manager_warning 
        ,p_hourly_salaried_warning      => lv_hourly_salaried_warning
      );

You should note that we have set three of the p_ass_attribute parameters to match the values expected in our configuration of HR.

      -- Get the new Object version number  
      select MAX(AA.OBJECT_VERSION_NUMBER)
      into lv_object_version_number
      from HR.PER_ALL_ASSIGNMENTS_F AA
      WHERE AA.person_id = person.person_id;
          
      lv_debug := 'Start second API.';
          
      -- before we can say that this record has been a success
      -- we need to call the next API, but this time as a correction.
      hr_assignment_api.update_emp_asg_criteria
        (p_effective_date               => career.effective_START_Date 
        ,p_datetrack_update_mode        => 'CORRECTION'
        ,p_assignment_id                => career.assignment_id
        ,p_validate                     => false
        ,p_called_from_mass_update      => false
        ,p_grade_id                     => career.grade_id
        ,p_position_id                  => TRIM(career.position_id)
        ,p_job_id                       => career.job_id
        ,p_payroll_id                   => career.payroll_id
        ,p_location_id                  => career.location_id
        ,p_organization_id              => career.organization_id
        ,p_pay_basis_id                 => career.pay_basis_id
        ,p_employment_category          => career.ASSIGNMENT_CATEGORY
        ,p_object_version_number        => lv_object_version_number
        ,p_special_ceiling_step_id      => lv_special_ceiling_step_id
        ,p_people_group_id              => career.people_group_id
        ,p_soft_coding_keyflex_id       => lv_soft_coding_keyflex_id
        ,p_group_name                   => lv_group_name
        ,p_effective_start_date         => lv_effective_start_date
        ,p_effective_end_date           => lv_effective_end_date
        ,p_org_now_no_manager_warning   => lv_org_now_no_manager_warning
        ,p_other_manager_warning        => lv_other_manager_warning
        ,p_spp_delete_warning           => lv_spp_delete_warning
        ,p_entries_changed_warning      => lv_entries_changed_warning
        ,p_tax_district_changed_warning => lv_tax_district_c_warning 
        ,p_concatenated_segments        => lv_concatenated_segments
        ,p_gsp_post_process_warning     => lv_gsp_post_process_warning
      );

The main reason we needed to call both APIs was that the position the assignment refers to is set in one API while the supervisor is set in the other. So every change in position that involves a change in manager needs both APIs. There are various other parameters that are set by one API but not the other.

          UPDATE my_apps.update_gb_emp_asg
          SET DATA_TRANSFERRED ='Y',
            error_message = lv_datetrack_update_mode
          WHERE assignment_id=career.assignment_id
          and effective_start_date = career.effective_start_date ;
          COMMIT;

        exception -- with either of the APIs
          when others then
            lv_error_message := sqlerrm;
   
            -- record what went wrong so we can fix it - hopefully!
            UPDATE my_apps.update_gb_emp_asg
            SET DATA_TRANSFERRED ='F', 
              error_message = lv_datetrack_update_mode || 
                ': ' || substr( lv_error_message, 1, 100 ) || 
                substr( lv_debug, 1, 80 )
            WHERE assignment_id = career.assignment_id
            and effective_start_date = career.effective_start_date ; 
            COMMIT;

The final bit is to record the results: either it all went well or one of the APIs raised an exception. Logging the sqlerrm allows us to find out what went wrong and hopefully fix it.

I'm sure you could improve on this substantially, but if you are starting from scratch like we had to this may save you quite a bit of time.