1 package body pay_za_calendars_pkg as
2 /* $Header: pyzapcal.pkb 120.2 2005/06/28 00:08:22 kapalani noship $ */
3 -- +======================================================================+
4 -- | Copyright (c) 1998 Oracle Corporation South Africa Ltd |
5 -- | Cape Town, Western Cape, South Africa |
6 -- | All rights reserved. |
7 -- +======================================================================+
8 -- SQL Script File Name : pyzapcal.pkb
9 -- Description : This sql script seeds the za_pay_calendars
10 -- package for the ZA localisation. This package
11 -- creates the payroll calendars needed for ZA.
12 --
13 -- Change List:
14 -- ------------
15 --
16 -- Name Date Version Bug Text
17 -- -------------- ---------- ------- ------- ------------------------------
18 -- F.D. Loubser 03-SEP-98 110.0 Initial Version
19 -- A. Mills 20-APR-99 110.1 Changed package name and
20 -- main interface procedure
21 -- (create_calendar) so that
22 -- dynamic call from core code
23 -- (hr_payrolls) will work.
24 -- F.D. Loubser 12-MAY-99 110.2 Added default value for
25 -- l_store_end_date in
26 -- create_za_employee_tax_years
27 -- F.D. Loubser 09-JUN-99 110.3 Changed 'PAY_' Messages to
28 -- conform to ZA standard
29 -- F.D. Loubser 11-JUN-99 110.4 Added move to last period
30 -- in Month End for first period
31 -- being start of tax year
32 -- J.N. Louw 23-AUG-2000 115.0 Updated package for 11i
33 -- Flexible Date Functionality
34 -- -- org_information11
35 -- L.J. Kloppers 22-Nov-2000 115.1 Move History Section into Package
36 -- L.J. Kloppers 27-Nov-2000 115.2 Added update of:
37 -- prd_information_category = 'ZA'
38 -- Ghanshyam 07-Jul-2002 115.0 Moved the contents from
39 -- $PER_TOP/patch/115/sql/hrzapcal.pkb
40 -- ========================================================================
41
42
43 ---------------------------------------------------------------------------------------
44 -- Global variables
45 ---------------------------------------------------------------------------------------
46
47 -- Package constants, denoting the base period types.
48 -- These constants are needed to call the core function add_multiple_of_base
49 WEEKLY CONSTANT varchar2(1) := 'W';
50 MONTHLY CONSTANT varchar2(1) := 'M';
51 SEMIMONTHLY CONSTANT varchar2(1) := 'S';
52
53 ---------------------------------------------------------------------------------------
54 -- This procedure is a driver that defaults the calendars for the payroll name
55 -- passed to it. It is only provided as a manual substitute, until the functionality
56 -- is incorporated into the core code.
57 -- Parameters:
58 -- p_payroll_name - The name of the payroll for which the calendars should be
59 -- defaulted.
60 -- p_effective_date - The effective date
61 -- NOTE: Error checking is not provided, since this procedure is not supposed to be
62 -- called as part of a core process.
63 ---------------------------------------------------------------------------------------
64
65 procedure do
66 (
67 p_payroll_name varchar2,
68 p_effective_date date
69 )
70 is
71
72 l_payroll_id number;
73 l_first_end_date date;
74 l_last_end_date date;
75 l_period_type varchar2(30);
76 l_period_type2 varchar2(1);
77 l_multiple number;
78 l_fpe date;
79
80 begin
81
82 -- Obtain the Payroll ID, First Period End Date and Period Type
83 select distinct
84 payroll_id, first_period_end_date, period_type
85 into
86 l_payroll_id, l_fpe, l_period_type
87 from
88 pay_payrolls_f
89 where
90 payroll_name = p_payroll_name
91 and
92 p_effective_date between effective_start_date and effective_end_date;
93
94 -- Obtain the chronologically first end date in the Payroll
95 select
96 min(end_date)
97 into
98 l_first_end_date
99 from
100 per_time_periods
101 where
102 payroll_id = l_payroll_id;
103
104 -- Obtain the chronologically last end date in the Payroll
105 select
106 max(end_date)
107 into
108 l_last_end_date
109 from
110 per_time_periods
111 where
112 payroll_id = l_payroll_id;
113
114 -- Determine the base period type and the multiple of the base
115 if l_period_type = 'Year' then
116 l_period_type2 := MONTHLY;
117 l_multiple := 12;
118 elsif l_period_type = 'Semi-Year' then
119 l_period_type2 := MONTHLY;
120 l_multiple := 6;
121 elsif l_period_type = 'Quarter' then
122 l_period_type2 := MONTHLY;
123 l_multiple := 3;
124 elsif l_period_type = 'Bi-Month' then
125 l_period_type2 := MONTHLY;
126 l_multiple := 2;
127 elsif l_period_type = 'Calendar Month' then
128 l_period_type2 := MONTHLY;
129 l_multiple := 1;
130 elsif l_period_type = 'Lunar Month' then
131 l_period_type2 := WEEKLY;
132 l_multiple := 4;
133 elsif l_period_type = 'Semi-Month' then
134 l_period_type2 := SEMIMONTHLY;
135 l_multiple := 1; -- Not used for semi-monthly
136 elsif l_period_type = 'Bi-Week' then
137 l_period_type2 := WEEKLY;
138 l_multiple := 2;
139 elsif l_period_type = 'Week' then
140 l_period_type2 := WEEKLY;
141 l_multiple := 1;
142 else
143 -- Unknown period type.
144 hr_utility.set_message(801, 'PAY_6601_PAYROLL_INV_PERIOD_TP');
145 hr_utility.raise_error;
146 end if;
147
148 -- Call the procedure that creates the ZA calendars with the necessary parameters.
149 create_calendar
150 (
151 l_payroll_id,
152 l_first_end_date,
153 l_last_end_date,
154 l_period_type,
155 l_period_type2,
156 l_multiple,
157 l_fpe
158 );
159 end do;
160
161 ---------------------------------------------------------------------------------------
162 -- This procedure is the driving procedure in the creation of the ZA Payroll
163 -- Calendars. It goes through the following steps:
167 -- 4. Generate the Payroll Month Ends between p_first_end_date and p_last_end_date
164 -- 1. Obtain the Business Group Id of the current user
165 -- 2. Obtain the day on which the Company Financial Year starts
166 -- 3. Obtain the date on which the Payroll Tax Year starts
168 -- 5. Generate the Tax Years between p_first_end_date and p_last_end_date
169 -- 6. Generate the Calendar Years between p_first_end_date and p_last_end_date
170 -- 7. Generate the Tax Quarters between p_first_end_date and p_last_end_date
171 -- Parameters:
172 -- p_payroll_id - The primary key ID of the payroll.
173 -- p_first_end_date - The first period end date in the generation time span.
174 -- p_last_end_date - The last period end date in the generation time span.
175 -- p_proc_period_type - The time period type, e.g. Week, Calendar Month.
176 -- p_base_period_type - The base period type, other period types are multiples of the
177 -- base period type, e.g. WEEKLY, MONTHLY, SEMIMONTHLY.
178 -- p_multiple - The amount of base periods that make up the period type.
179 -- p_fpe_date - The first period end date in the payroll time span.
180 ---------------------------------------------------------------------------------------
181 procedure create_calendar
182 (
183 p_payroll_id in number,
184 p_first_end_date in date,
185 p_last_end_date in date,
186 p_proc_period_type in varchar2,
187 -- Parameters needed to call the core function add_multiple_of_base
188 p_base_period_type in varchar2,
189 p_multiple in number,
190 p_fpe_date in date
191 )
192 is
193 -- The Business Group Id of the current user
194 l_business_group_id number;
195 -- The day on which the Company Financial Year Starts
196 l_fiscal_start_day number;
197 -- The date in DD-MON format on which the Payroll Tax Year starts
198 l_tax_year_start varchar2(50);
199 -- Function name used in trace package
200 func_name CONSTANT varchar2(50) := 'za_pay_calendars.create_calendar';
201
202 begin
203
204 hr_utility.set_location(func_name, 1);
205 -- Obtain the Business Group Id of the current user
206 l_business_group_id := fnd_profile.value('PER_BUSINESS_GROUP_ID');
207 -- Obtain the day on which the Company Financial Year starts
208 l_fiscal_start_day := to_number(
209 to_char(retrieve_fiscal_year_start(l_business_group_id), 'DD'));
210 --
211 -- Obtain the date on which the Payroll Tax Year starts
212 l_tax_year_start := retrieve_tax_year_start;
213
214 -- Generate the Payroll Month Ends between p_first_end_date and p_last_end_date
215 create_za_payroll_month_ends
216 (
217 p_payroll_id,
218 p_first_end_date,
219 p_last_end_date,
220 l_fiscal_start_day,
221 p_base_period_type,
222 p_multiple,
223 p_fpe_date
224 );
225
226 -- Generate the Tax Years between p_first_end_date and p_last_end_date
227 create_za_employee_tax_years
228 (
229 p_payroll_id,
230 p_first_end_date,
231 p_last_end_date,
232 l_tax_year_start,
233 p_proc_period_type
234 );
235
236 -- Generate the Calendar Years between p_first_end_date and p_last_end_date
237 create_za_employee_cal_years
238 (
239 p_payroll_id,
240 p_first_end_date,
241 p_last_end_date,
242 p_proc_period_type
243 );
244
245 -- Generate the Tax Quarters between p_first_end_date and p_last_end_date
246 create_za_tax_quarters
247 (
248 p_payroll_id,
249 p_first_end_date,
250 p_last_end_date
251 );
252
253 end create_calendar;
254
255 ---------------------------------------------------------------------------------------
256 -- This function is called to retrieve the Payroll Tax Year Start from the table
257 -- pay_legislation_rules.
258 -- NOTE: The function makes sure that the Payroll Tax Year is in DD-MON format.
259 ---------------------------------------------------------------------------------------
260 function retrieve_tax_year_start return varchar2 is
261
262 -- The Payroll Tax Year start date in DD-MON format.
263 l_tax_year_start varchar2(50);
264 -- The day of the Payroll Tax Year
265 l_tax_day number;
266 -- The month of the Payroll Tax Year
267 l_tax_month varchar2(50);
268 -- Function name used in trace package
269 func_name CONSTANT varchar2(50) := 'za_pay_calendars.retrieve_tax_year_start';
270
271 begin
272
273 hr_utility.set_location(func_name, 1);
274 -- Obtain the Tax Year Start from pay_legislation_rules
275 select
276 rule_mode
277 into
278 l_tax_year_start
279 from
280 pay_legislation_rules
281 where
282 legislation_code = 'ZA'
283 and
284 rule_type = 'L';
285
286 -- Determine the day of the Tax Year
287 l_tax_day :=
288 to_number(substr(l_tax_year_start, 1, instr(l_tax_year_start, '-') - 1));
289 -- Check whether this is a valid day
290 if l_tax_day > 31 then
291 raise invalid_number;
292 end if;
293
294 -- Determine the month of the Tax Year
295 l_tax_month := substr(l_tax_year_start, instr(l_tax_year_start, '-') + 1, 3);
296 -- Check whether this is a valid month
297 if instr('JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC', l_tax_month) = 0 then
298 raise invalid_number;
299 end if;
300
301 return l_tax_year_start;
302
303 exception
304 -- The Tax Year is not in DD-MON format
305 when no_data_found then
306 hr_utility.set_message(800, 'PAY_PAYROLL_TAX_YEAR');
307 hr_utility.raise_error;
308 when too_many_rows then
309 hr_utility.set_message(800, 'PAY_PAYROLL_TAX_YEAR');
310 hr_utility.raise_error;
311 when invalid_number then
312 hr_utility.set_message(800, 'PAY_PAYROLL_TAX_YEAR');
313 hr_utility.raise_error;
314
315 end retrieve_tax_year_start;
316
317 ---------------------------------------------------------------------------------------
318 -- This function is called to retrieve the Company Fiscal Year Start for the
319 -- current Business Group.
320 -- Parameters:
321 -- p_business_group_id - The Business Group ID of the currently logged in user.
322 -- NOTE: The flexfield already forces this value to DD-MON-YYYY format.
323 ---------------------------------------------------------------------------------------
324 function retrieve_fiscal_year_start
325 (
326 p_business_group_id in number
327 )
328 return date is
329
330 -- The start date of the Fiscal year
331 l_fiscal_start date;
332 -- Function name used in trace package
333 func_name CONSTANT varchar2(50) := 'za_pay_calendars.retrieve_fiscal_year_start';
334
335 begin
336
337 hr_utility.set_location(func_name, 1);
338 -- Determine the Company Fiscal Year Start for the current Business Group
339 select
340 fnd_date.canonical_to_date(org_information11)
341 into
342 l_fiscal_start
346 organization_id = p_business_group_id
343 from
344 hr_organization_information
345 where
347 and
348 org_information_context = 'Business Group Information';
349
350 return l_fiscal_start;
351
352 exception
353 when no_data_found then
354 hr_utility.set_message(800, 'PAY_COMPANY_FISCAL_YEAR');
355 hr_utility.raise_error;
356
357 end retrieve_fiscal_year_start;
358
359 ---------------------------------------------------------------------------------------
360 -- This procedure creates the Payroll Month Ends for the ZA calendars, by using the
361 -- following steps:
362 -- 1. Loop through the periods.
363 -- 2. Calculate the Company Financial Month Start for the current period.
364 -- 3. Store the Financial Month Start and End Date of the current period.
365 -- 4. If the Financial Month Start of the current period is different from that
366 -- of the previous period, then the previous period is a Payroll Month End.
367 -- Update all the periods between l_store_end_date and the previous period,
368 -- so that their Payroll Month End is equal to the Period End Date of the
369 -- previous period. Store the current period's end in l_store_end_date.
370 -- NOTE: For the last few periods you might have to generate extra periods, using
371 -- add_multiple_of_base, in order to find the Payroll Month End.
372 -- Parameters:
373 -- p_payroll_id - The primary key ID of the payroll.
374 -- p_first_end_date - The first period end date in the generation time span.
375 -- p_last_end_date - The last period end date in the generation time span.
376 -- p_fiscal_start_day - The day on which the Company Fiscal Year starts.
377 -- p_base_period_type - The base period type, other period types are multiples of the
378 -- base period type, e.g. WEEKLY, MONTHLY, SEMIMONTHLY.
379 -- p_multiple - The amount of base periods that make up the period type.
380 -- p_fpe_date - The first period end date in the payroll time span.
381 ---------------------------------------------------------------------------------------
382 procedure create_za_payroll_month_ends
383 (
384 p_payroll_id in number,
385 p_first_end_date in date,
386 p_last_end_date in date,
387 p_fiscal_start_day in number,
388 -- Parameters needed to call the core function add_multiple_of_base
389 p_base_period_type in varchar2,
390 p_multiple in number,
391 p_fpe_date in date
392 )
393 is
394
395 -- Temporary variable used to store the previous period's end date.
396 l_previous_end_date date;
397 -- Temporary variable used to store the end date where the Payroll Month starts.
398 l_store_end_date date;
399 -- Temporary variable used to store the previous period's end date.
400 l_old_end_date date;
401 -- Used to store the current period's end date.
402 l_end_date date;
403 -- Temporary variable used to store the Fiscal Month Start.
404 l_store_fin_ms date;
405 -- Used to store the current period's Fiscal Month Start.
406 l_fin_ms date;
407 -- Function name used in trace package
408 func_name CONSTANT varchar2(50) := 'za_pay_calendars.za_payroll_month_ends';
409
410 -- Cursor that holds all the periods in the generation time span.
411 cursor get_periods is
412 select
413 end_date
414 from
415 per_time_periods
416 where
417 payroll_id = p_payroll_id
418 and
419 end_date between p_first_end_date and p_last_end_date
420 order by
421 end_date;
422
423 begin
424
425 hr_utility.set_location(func_name, 1);
426 open get_periods;
427 -- Store the first period end date
428 fetch get_periods into l_store_end_date;
429
430 -- Check whether there are any periods to process
431 if get_periods%notfound then
432 -- No periods to generate Month Ends for.
433 null;
434 else
435 -- Calculate the Company Financial Month Start for the first period and store it
436 l_store_fin_ms :=
437 generate_fiscal_month_start(l_store_end_date, p_fiscal_start_day);
438
439 -- Store the previous period end date
440 l_previous_end_date := l_store_end_date;
441
442 -- Loop through the remaining periods
443 loop
444 fetch get_periods into l_end_date;
445 exit when get_periods%notfound;
446
447 -- Calculate the Company Financial Month Start for the current period and
448 -- compare it with the previous one
449 l_fin_ms :=
450 generate_fiscal_month_start(l_end_date, p_fiscal_start_day);
451
452 -- If the Company Financial Month Starts differ, then the previous period is a
453 -- Payroll Month End
454 if l_store_fin_ms <> l_fin_ms then
455 -- Update all the periods between l_store_end_date and the previous
456 -- period, so that their Payroll Month End is equal to the Period End Date
457 -- of the previous period
458 update
459 per_time_periods
460 set
461 pay_advice_date = l_previous_end_date
462 where
463 payroll_id = p_payroll_id
464 and
465 end_date between l_store_end_date and l_previous_end_date;
466
467 -- Set l_store_end_date to the current period's end date
468 l_store_end_date := l_end_date;
469 end if;
470
471 -- Store the previous period's end date
472 l_previous_end_date := l_end_date;
473 l_store_fin_ms := l_fin_ms;
474 end loop;
475
476 -- For the last few periods there might not exist enough periods in the payroll
477 -- time span to obtain the Payroll Month End, therefore you have to obtain the
478 -- Payroll Month End for these periods by generating dummy periods using the
479 -- add_multiple_of_base function.
480 if l_store_end_date <= p_last_end_date then
481
482 -- Save the period to update from
483 l_end_date := l_store_end_date;
484
485 -- Loop until you find a Period End Date that is bigger than the current
486 -- Company Financial Month Start
487 while l_store_end_date < l_store_fin_ms loop
488
489 -- Store the old end date
490 l_old_end_date := l_store_end_date;
491
492 -- Determine the next period's end date
493 l_store_end_date := add_multiple_of_base(l_store_end_date,
494 p_base_period_type,
495 p_multiple,
496 p_fpe_date);
497 end loop;
498
499 -- Update all the periods between l_store_end_date and the last period,
500 -- so that their Payroll Month End is equal to l_old_end_date
501 update
502 per_time_periods
503 set
504 pay_advice_date = l_old_end_date
505 where
506 payroll_id = p_payroll_id
507 and
508 end_date between l_end_date and p_last_end_date;
509
510 end if;
511 end if;
512 close get_periods;
513 end create_za_payroll_month_ends;
514
515 ---------------------------------------------------------------------------------------
516 -- This function returns the Company Fiscal Month Start associated with the payroll
517 -- period. It returns the first date that falls on the day on which the Company
518 -- Fiscal Year start, and which is larger than the period's end date.
519 -- Parameters:
520 -- p_end_date - The period end date of the pay period.
521 -- p_fiscal_start_day - The day on which the Company Fiscal Year starts.
522 ---------------------------------------------------------------------------------------
523 function generate_fiscal_month_start
524 (
525 p_end_date in date,
526 p_fiscal_start_day in number
527 )
528 return date is
529
530 -- Temporary variable that holds the Fiscal Month Start.
531 l_fiscal_ms date;
532 -- Function name used in trace package
533 func_name CONSTANT varchar2(50) := 'za_pay_calendars.generate_fiscal_month_start';
534
535 begin
536
537 hr_utility.set_location(func_name, 1);
538 -- As a first try, try using the day of the fiscal year and the month and year
539 -- of the period's end date
540 -- next_lower_day checks whether the date exist, otherwise it subtracts days until
541 -- the date does exist
542 l_fiscal_ms := next_lower_day(p_fiscal_start_day, p_end_date);
543
544 -- The date must be larger than the period's end date, otherwise you must add a
545 -- month
546 while l_fiscal_ms <= p_end_date loop
547 l_fiscal_ms := next_lower_day(p_fiscal_start_day, add_months(l_fiscal_ms, 1));
548 end loop;
549 return l_fiscal_ms;
550 end generate_fiscal_month_start;
551
552 ---------------------------------------------------------------------------------------
553 -- This function returns p_fiscal_start_day concatenated with p_date's month and
554 -- year, if it exist, else it returns the next lower date that exist.
555 -- Parameters:
556 -- p_fiscal_start_day - The day on which the Fiscal Year starts.
557 -- p_date - A valid date that has the month and year of the date to be
558 -- returned.
559 ---------------------------------------------------------------------------------------
560 function next_lower_day
561 (
562 p_fiscal_start_day in number,
563 p_date in date
564 )
565 return date is
566
567 -- The last day of the month.
568 l_last_day number;
569 -- The date of the last day of the month.
570 l_last date;
571 -- Function name used in trace package
572 func_name CONSTANT varchar2(50) := 'za_pay_calendars.next_lower_day';
573
574 begin
575
576 hr_utility.set_location(func_name, 1);
577 -- Determine the date of the last day of the month
578 l_last := last_day(p_date);
579
580 -- Determine the day of the last day of the month
581 l_last_day := to_char(l_last, 'DD');
582
583 -- If the day of the date is larger than the last day of the month,
584 -- return the last day of the month, else return the date
585 if p_fiscal_start_day >= l_last_day then
586 return l_last;
587 else
588 return to_date(to_char(p_fiscal_start_day) || '-' ||
589 to_char(p_date, 'MM') || '-' ||
590 to_char(p_date, 'YYYY'), 'DD-MM-YYYY');
591 end if;
592
593 end next_lower_day;
594
598 -- calculation can be performed by adding days ie. straightforward addition
595 ---------------------------------------------------------------------------------------
596 -- This function performs the date calculation according to the base period type for
597 -- the period type being considered. Note that for WEEKLY base period, the
599 -- operation. For MONTHLY base period, the add_months() function is used. The
600 -- exception to these general categories is semi-monthly, which is handled explicitly
601 -- by the SEMIMONTHLY base period. This case only uses the p_multiple parameter to
602 -- determine whether to add or subtract a semi-month, ie. this function can only
603 -- increase semi-months unitarily. Furthermore, it has exclusive use of the
604 -- 'regular_pay_mode' parameter, which for SEMIMONTHLY, indicates whether or not we
605 -- are calculating for a regular payment date calculation. The p_fpe_date is the
606 -- first period's end-date and is needed only for semimonthly period calculations.
607 -- NOTE: This function is a copy of the core function in the hr_payrolls package,
608 -- which does not have a public interface.
609 ---------------------------------------------------------------------------------------
610 function add_multiple_of_base
611 (
612 p_target_date in date,
613 p_base_period_type in varchar2,
614 p_multiple in number,
615 p_fpe_date in date,
616 p_regular_pay_mode boolean default false,
617 p_pay_date_offset number default null
618 )
619 return date is
620
621 func_name CONSTANT varchar2(50) := 'za_pay_calendars.add_multiple_of_base';
622 rest_of_date varchar2(9);
623 temp_date date;
624
625 begin
626
627 -- Errors can occur when performing date manipulation.
628 if p_base_period_type = WEEKLY then
629 hr_utility.set_location(func_name, 1);
630 return (p_target_date + (7 * p_multiple));
631 elsif p_base_period_type = MONTHLY then
632 hr_utility.set_location(func_name, 2);
633 return (add_months(p_target_date, p_multiple));
634 else
635 -- This is semi-monthly. A pair of semi-months always spand a whole calendar
636 -- month. Their start and end dates are either 1st - 15th or 16th - last day of
637 -- month. This makes the addition/subtraction of a period reasonably
638 -- straightforward, if a little involved.
639 -- On the other hand, if the p_regular_pay_mode is TRUE, we have to take into
640 -- account the regular pay date offset.
641 if not p_regular_pay_mode then
642 if p_multiple > 0 then
643 -- Addition of one semi-month.
644 return(next_semi_month(p_target_date, p_fpe_date));
645 else
646 -- Substraction of one semi-month.
647 return(prev_semi_month(p_target_date, p_fpe_date));
648 end if;
649 else
650 -- Special calculation for regular payment date offset. In this special case,
651 -- we calculate the pay date of the previous semimonth period: Current
652 -- pay-date -> Current end-date -> Previous period's end-date -> Previous
653 -- period's pay-date.
654 temp_date := prev_semi_month(p_target_date - p_pay_date_offset,
655 p_fpe_date) + p_pay_date_offset;
656 hr_utility.trace('pay date ' || to_char(temp_date, 'dd-mon-yyyy'));
657 return(temp_date);
658 -- OLD CODE
659 -- Now period's are not guaranteed to end on the 15th or the end_of_month.
660 --
661 -- rest_of_date := substr(to_char(p_target_date, 'DD-MON-YYYY'), 3);
662 -- if p_target_date between
663 -- to_date(('01' || rest_of_date), 'DD-MON-YYYY') and
664 -- to_date(('15' || rest_of_date), 'DD-MON-YYYY')
665 -- then
666 -- -- First half of month, put to 15th and subtract a period.
667 -- temp_date := to_date(('15' || rest_of_date), 'DD-MON-YYYY');
668 -- temp_date := prev_semi_month(temp_date) + p_pay_date_offset;
669 -- hr_utility.trace('1st half ' || to_char(temp_date, 'dd-mon-yyyy'));
670 -- return(temp_date);
671 -- else
672 -- -- We are in second half of month.
673 -- temp_date := last_day(p_target_date);
674 -- temp_date := prev_semi_month(temp_date) + p_pay_date_offset;
675 -- hr_utility.trace('2nd half ' || to_char(temp_date, 'dd-mon-yyyy'));
676 -- return(temp_date);
677 -- end if;
678 -- OLD CODE
679 end if;
680 end if;
681 end add_multiple_of_base;
682
683 ---------------------------------------------------------------------------------------
684 -- Locally defined function that, given the end-date of a semi-month period and the
685 -- first period's end-date (p_fpe_date) returns the end date of the following
686 -- semi-monthly period.
687 -- NOTE: This function is a copy of the core function in the hr_payrolls package,
688 -- which does not have a public interface.
689 ---------------------------------------------------------------------------------------
690 function next_semi_month
691 (
692 p_semi_month_date in date,
693 p_fpe_date in date
694 )
695 return date is
696
697 day_of_month varchar2(2);
698 last_of_month date;
699 temp_day varchar2(2);
700 func_name CONSTANT varchar2(50) := 'za_pay_calendars.next_semi_month';
701
702 begin
703 hr_utility.set_location(func_name, 1);
704 day_of_month := substr(to_char(p_fpe_date, 'DD-MON-YYYY'), 1, 2);
705 if (day_of_month = '15') OR (last_day(p_fpe_date) = p_fpe_date) then
706 -- The first period's end-date is either the 15th or the end-of-month
707 if last_day(p_semi_month_date) = p_semi_month_date then
711 -- 15th of month: return last day
708 -- End of month: add 15 days
709 return(p_semi_month_date + 15);
710 else
712 return(last_day(p_semi_month_date));
713 end if;
714 else
715 -- The first period's end-date is neither the 15th nor the end-of-month
716 -- temp_day = smaller of the 2 day numbers used to calc period end-dates
717 temp_day := day_of_month ;
718 if temp_day > '15' then
719 temp_day := substr(to_char(p_fpe_date - 15, 'DD-MON-YYYY'), 1, 2);
720 end if ;
721 --
722 day_of_month := substr(to_char(p_semi_month_date, 'DD-MON-YYYY'), 1, 2);
723 if day_of_month between '01' AND '15' then
724 if last_day(p_semi_month_date+15) = last_day(p_semi_month_date) then
725 return(p_semi_month_date + 15);
726 else
727 -- for p_semi_month_date = Feb 14th, for example
728 return(last_day(p_semi_month_date));
729 end if;
730 else -- if on the 16th or later
731 return(to_date( (temp_day ||
732 substr(to_char(add_months(p_semi_month_date,1),'DD-MON-YYYY'),3)
733 ), 'DD-MON-YYYY'));
734 end if ;
735 end if ;
736 end next_semi_month;
737
738 ---------------------------------------------------------------------------------------
739 -- Locally defined function that, given the end-date of a semi-month period and the
740 -- first period's end-date (p_fpe_date) returns the end date of the previous
741 -- semi-monthly period.
742 -- NOTE: This function is a copy of the core function in the hr_payrolls package,
743 -- which does not have a public interface.
744 ---------------------------------------------------------------------------------------
745 function prev_semi_month
746 (
747 p_semi_month_date in date,
748 p_fpe_date in date
749 )
750 return date is
751
752 day_of_month varchar2(2);
753 last_of_month date;
754 temp_date date;
755 temp_day varchar2(2);
756 func_name CONSTANT varchar2(50) := 'za_pay_calendars.prev_semi_month';
757
758 begin
759 -- Get the day of the current month.
760 hr_utility.set_location(func_name, 1);
761 day_of_month := substr(to_char(p_fpe_date, 'DD-MON-YYYY'), 1, 2);
762 if (day_of_month = '15') OR (last_day(p_fpe_date) = p_fpe_date) then
763 -- The first period's end-date is either the 15th or the end-of-month
764 if last_day(p_semi_month_date) = p_semi_month_date then
765 -- End of month: return 15th of current month
766 return(add_months(p_semi_month_date,-1) + 15);
767 else
768 -- 15th of month: return last day of previous month
769 return(last_day(add_months(p_semi_month_date,-1)));
770 end if;
771 else
772 -- The first period's end-date is neither the 15th nor the end-of-month
773 -- temp_day = smaller of the 2 day numbers used to calc period end-dates
774 temp_day := day_of_month ;
775 if temp_day > '15' then
776 temp_day := substr(to_char(p_fpe_date - 15, 'DD-MON-YYYY'), 1, 2);
777 end if ;
778 --
779 day_of_month := substr(to_char(p_semi_month_date, 'DD-MON-YYYY'), 1, 2);
780 if day_of_month between '01' AND '15' then
781 temp_date := add_months (p_semi_month_date, -1) ;
782 if last_day(temp_date+15) = last_day(temp_date) then
783 return(temp_date + 15);
784 else
785 -- for p_semi_month_date = Mar 14th, for example
786 return(last_day(temp_date));
787 end if;
788 else -- if after the 16th
789 return(to_date( (temp_day ||
790 substr(to_char(p_semi_month_date,'DD-MON-YYYY'),3)
791 ), 'DD-MON-YYYY'));
792 end if ;
793 end if ;
794 end prev_semi_month;
795
796 ---------------------------------------------------------------------------------------
797 -- This procedure creates the payroll tax years for the ZA calendars, by using the
798 -- following steps:
799 -- 1. Determine the first tax year start in the generation period:
800 -- 2. Check whether the previous, current or next year is closest to the first
801 -- period's start date.
802 -- 3. If the first period's Payroll Month End is within 14 days of the Tax Year
803 -- End, then use that Month End as the first Employee Tax Year End;
804 -- else,
805 -- if the Payroll Month End is larger than the Tax Year End, add a year to the
806 -- Tax Year End. Loop forward through the periods until you find a Payroll
807 -- Month End that is larger than the Tax Year End, and determine whether the
808 -- current Payroll Month End or the previous one is closer to the Tax Year
809 -- End. Use the closest Payroll Month End as the first Employee Tax Year End.
810 -- 4. Update all the periods up to the end of this Payroll Month End with the
811 -- first Tax Year.
812 -- 5. Default the Tax Year for the rest of the periods:
813 -- 6. Get the number of periods in the year from per_time_period_types.
814 -- 7. Loop through all the remaining periods.
815 -- 8. Jump forward by the amount of periods in the year, and keep moving
816 -- forward until you find the last period in that Month End. Update the
817 -- periods in between to the next Tax year.
818 -- 9. Go to the beginning of the loop.
819 -- 10. If there are not enough periods to move forward by an entire year, then just
820 -- update the remaining periods to the next Tax Year.
821 -- ASSUMPTION: The tax year start is in 'DD-MON' format.
822 -- Parameters:
823 -- p_payroll_id - The primary key ID of the payroll.
827 -- p_proc_period_type - The processing period type, e.g. Week, Calendar Month.
824 -- p_first_end_date - The first period end date in the generation time span.
825 -- p_last_end_date - The last period end date in the generation time span.
826 -- p_tax_year_start - The date on which the tax year starts in DD-MON format.
828 ---------------------------------------------------------------------------------------
829 procedure create_za_employee_tax_years
830 (
831 p_payroll_id in number,
832 p_first_end_date in date,
833 p_last_end_date in date,
834 p_tax_year_start in varchar2,
835 p_proc_period_type in varchar2
836 )
837 is
838
839 -- Variable used to hold the start date.
840 l_start_date date;
841 -- Variable used to hold the end date.
842 l_end_date date;
843 -- Variable used to store the end date.
844 l_store_end_date date := p_first_end_date;
845 -- Variable used to store the end date.
846 l_old_end_date date;
847 -- Variable used to hold the Payroll Month End.
848 l_pay_me date;
849 -- Variable used to store the Payroll Month End.
850 l_old_pay_me date;
851 -- Variable used to hold the Tax Year.
852 l_tax_year date;
853 -- Variable used to move a certain amount of rows forward.
854 l_cur_row number;
855 -- The number of periods in the year.
856 l_no_periods number;
857 -- Function name used in trace package
858 func_name CONSTANT varchar2(50) := 'za_pay_calendars.create_za_employee_tax_years';
859 -- Check whether a year should be added
860 l_flag number := 0;
861
862 -- Cursor that holds all the periods in the generation time span.
863 cursor get_periods is
864 select
865 end_date, pay_advice_date
866 from
867 per_time_periods
868 where
869 payroll_id = p_payroll_id
870 and
871 end_date between p_first_end_date and p_last_end_date
872 order by
873 end_date;
874
875 -- Cursor that looks for a previous payroll month end
876 cursor get_previous_periods is
877 select
878 end_date, pay_advice_date
879 from
880 per_time_periods
881 where
882 payroll_id = p_payroll_id
883 and
884 end_date < p_first_end_date
885 order by
886 end_date desc;
887
888 begin
889
890 ---------------------------------------------------------------------------------------
891 -- Determine the first tax year start in the generation period
892 ---------------------------------------------------------------------------------------
893 hr_utility.set_location(func_name, 1);
894 declare
895 l_tax_year2 date;
896 begin
897 -- Obtain the first period's start date
898 select
899 start_date
900 into
901 l_start_date
902 from
903 per_time_periods
904 where
905 payroll_id = p_payroll_id
906 and
907 end_date = p_first_end_date;
908
909 -- Concatenate p_tax_year_start with the year of the start_date
910 l_tax_year := to_date(p_tax_year_start ||
911 to_char(l_start_date, 'YYYY'), 'DD-MON-YYYY');
912
913 -- Determine the next year's Tax year start
914 l_tax_year2 := to_date(p_tax_year_start ||
915 to_char(add_months(l_start_date, 12), 'YYYY'),
916 'DD-MON-YYYY');
917
918 -- Determine which one of the Tax Year Starts are the closest to the start date
919 if abs(l_tax_year2 - l_start_date) < abs(l_tax_year - l_start_date) then
920 l_tax_year := l_tax_year2;
921 end if;
922
923 -- Convert to a Tax Year End
924 l_tax_year := l_tax_year - 1;
925
926 exception
927 when no_data_found then
928 -- Create a dummy, since it won't be used anyway
929 l_tax_year := to_date('01-03-1001', 'DD-MM-YYYY');
930 end;
931
932 open get_periods;
933 fetch get_periods into l_end_date, l_old_pay_me;
934 if get_periods%notfound then
935 -- No periods to generate Tax Years for.
936 null;
937 else
938 -- If the first period's Payroll Month End is within 14 days of the Tax Year
939 -- End, then use that Month End as the first Employee Tax Year End
940 if abs(l_old_pay_me-l_tax_year) < 15 then
941 l_tax_year := l_old_pay_me;
942
943 -- Find the last end date in the Payroll Month End
944 select
945 max(end_date)
946 into
947 l_store_end_date
948 from
949 per_time_periods
950 where
951 payroll_id = p_payroll_id
952 and
953 pay_advice_date = l_old_pay_me;
954 else
955 -- If the Payroll Month End is larger than the Tax Year End, then add a year
956 -- to the Tax Year End
957 if l_old_pay_me > l_tax_year then
958 l_tax_year := add_months(l_tax_year, 12);
959 end if;
960
961 loop
962 -- Get the current period's Payroll Month End
963 fetch get_periods into l_end_date, l_pay_me;
964 if get_periods%notfound then
965 l_flag := 1;
966 end if;
967 exit when get_periods%notfound;
968
969 -- If the current Payroll Month End is larger than the Tax Year End, then
973
970 -- decide whether this Payroll Month End or the previous one is closer to
971 -- the Tax Year End. Use that Month End as the first Employee Tax Year End.
972 if l_pay_me > l_tax_year then
974 -- Determine whether this Payroll Month End or the previous one is
975 -- closer to the Tax Year End.
976 if abs(l_old_pay_me - l_tax_year) < abs(l_pay_me - l_tax_year) then
977 l_tax_year := l_old_pay_me;
978 l_store_end_date := l_old_end_date;
979 else
980 l_tax_year := l_pay_me;
981
982 -- Find the last end date in the Payroll Month End
983 select
984 max(end_date)
985 into
986 l_store_end_date
987 from
988 per_time_periods
989 where
990 payroll_id = p_payroll_id
991 and
992 pay_advice_date = l_pay_me;
993 end if;
994
995 -- Exit the loop
996 exit;
997
998 end if;
999
1000 -- Store the previous Payroll Month End and End Date
1001 l_old_pay_me := l_pay_me;
1002 l_old_end_date := l_end_date;
1003 end loop;
1004 end if;
1005
1006 -- Update the first periods to the first tax year
1007 update
1008 per_time_periods
1009 set
1010 prd_information1 = to_char(l_tax_year, 'YYYY'),
1011 prd_information_category = 'ZA'
1012 where
1013 payroll_id = p_payroll_id
1014 and
1015 end_date between p_first_end_date and l_store_end_date;
1016 end if;
1017 close get_periods;
1018
1019 ---------------------------------------------------------------------------------------
1020 -- Now default the tax year for the rest of the periods
1021 ---------------------------------------------------------------------------------------
1022 open get_periods;
1023 fetch get_periods into l_end_date, l_pay_me;
1024 if get_periods%notfound then
1025 -- No periods to generate Tax Years for.
1026 null;
1027 else
1028 -- Move to the next period to process
1029 while l_end_date <= l_store_end_date loop
1030 -- Get the current period's details
1031 fetch get_periods into l_end_date, l_pay_me;
1032 exit when get_periods%notfound;
1033 end loop;
1034
1035 -- If there are no more periods to process then do nothing
1036 if get_periods%notfound then
1037 -- This should never happen
1038 null;
1039 else
1040 begin
1041 -- Get the number of periods in the year
1042 select
1043 number_per_fiscal_year
1044 into
1045 l_no_periods
1046 from
1047 per_time_period_types
1048 where
1049 period_type = p_proc_period_type;
1050
1051 exception
1052 -- No details exists for this period type
1053 when no_data_found then
1054 hr_utility.set_message(800, 'PAY_NO_PERIOD_DETAILS');
1055 hr_utility.raise_error;
1056 end;
1057
1058 -- Loop through all the periods until you are finished
1059 while not get_periods%notfound loop
1060
1061 -- Estimate the location of the next tax year, by adding the amount of
1062 -- payroll periods in the tax year to the current position
1063 l_cur_row := get_periods%rowcount + l_no_periods - 1;
1064
1065 -- Save the current end date
1066 l_old_end_date := l_end_date;
1067
1068 -- Determine the current tax year
1069 if l_flag = 1 then
1070 l_flag := 0;
1071 else
1072 l_tax_year := add_months(l_tax_year, 12);
1073 end if;
1074
1075 -- Move forward by the amount of payroll periods in the tax year
1076 while get_periods%rowcount < l_cur_row loop
1077 -- Get the current period's details
1078 fetch get_periods into l_end_date, l_pay_me;
1079 exit when get_periods%notfound;
1080 end loop;
1081
1082 -- If we got to the end of the cursor then we have to update the tax year up
1083 -- to the end of the cursor, otherwise move forward until you find the last
1084 -- period in that Payroll Month End
1085 if get_periods%notfound then
1086 -- Update the periods up to the last period end date
1087 update
1088 per_time_periods
1089 set
1090 prd_information1 = to_char(l_tax_year, 'YYYY'),
1091 prd_information_category = 'ZA'
1092 where
1093 payroll_id = p_payroll_id
1094 and
1095 end_date between l_old_end_date and p_last_end_date;
1096 else
1097 -- Find the last period in the current Payroll Month End
1098 l_old_pay_me := l_pay_me;
1099 while l_pay_me = l_old_pay_me loop
1100 fetch get_periods into l_end_date, l_pay_me;
1101 exit when get_periods%notfound;
1102 -- Save the previous end date
1103 l_store_end_date := l_end_date;
1104 end loop;
1105
1106 if get_periods%notfound then
1110 set
1107 -- Update the periods up to the last period end date
1108 update
1109 per_time_periods
1111 prd_information1 = to_char(l_tax_year, 'YYYY'),
1112 prd_information_category = 'ZA'
1113 where
1114 payroll_id = p_payroll_id
1115 and
1116 end_date between l_old_end_date and p_last_end_date;
1117 else
1118 -- Update the periods up to the previous period
1119 update
1120 per_time_periods
1121 set
1122 prd_information1 = to_char(l_tax_year, 'YYYY'),
1123 prd_information_category = 'ZA'
1124 where
1125 payroll_id = p_payroll_id
1126 and
1127 end_date between l_old_end_date and l_store_end_date;
1128 end if;
1129 end if;
1130 end loop;
1131 end if;
1132 end if;
1133 close get_periods;
1134 end create_za_employee_tax_years;
1135
1136 ---------------------------------------------------------------------------------------
1137 -- This procedure creates the payroll calendar years for the ZA calendars, by using
1138 -- the following steps:
1139 -- 1. Determine the first calendar year start in the generation period:
1140 -- 2. Check whether the previous, current or next year is closest to the first
1141 -- period's start date.
1142 -- 3. If the first period's Payroll Month End is within 14 days of the Cal Year
1143 -- End, then use that Month End as the first Employee Cal Year End;
1144 -- else,
1145 -- if the Payroll Month End is larger than the Cal Year End, add a year to the
1146 -- Cal Year End. Loop forward through the periods until you find a Payroll
1147 -- Month End that is larger than the Cal Year End, and determine whether the
1148 -- current Payroll Month End or the previous one is closer to the Cal Year
1149 -- End. Use the closest Payroll Month End as the first Employee Cal Year End.
1150 -- 4. Update all the periods up to the end of this Payroll Month End with the
1151 -- first Cal Year.
1152 -- 5. Default the Cal Year for the rest of the periods:
1153 -- 6. Get the number of periods in the year from per_time_period_types.
1154 -- 7. Loop through all the remaining periods.
1155 -- 8. Jump forward by the amount of periods in the year, and keep moving
1156 -- forward until you find the last period in that Month End. Update the
1157 -- periods in between to the next Cal year.
1158 -- 9. Go to the beginning of the loop.
1159 -- 10. If there are not enough periods to move forward by an entire year, then just
1160 -- update the remaining periods to the next Cal Year.
1161 -- Parameters:
1162 -- p_payroll_id - The primary key ID of the payroll.
1163 -- p_first_end_date - The first period end date in the generation time span.
1164 -- p_last_end_date - The last period end date in the generation time span.
1165 -- p_proc_period_type - The processing period type, e.g. Week, Calendar Month.
1166 ---------------------------------------------------------------------------------------
1167 procedure create_za_employee_cal_years
1168 (
1169 p_payroll_id in number,
1170 p_first_end_date in date,
1171 p_last_end_date in date,
1172 p_proc_period_type in varchar2
1173 )
1174 is
1175
1176 -- Variable used to hold the start date.
1177 l_start_date date;
1178 -- Variable used to hold the end date.
1179 l_end_date date;
1180 -- Variable used to store the end date.
1181 l_store_end_date date;
1182 -- Variable used to store the end date.
1183 l_old_end_date date;
1184 -- Variable used to hold the Payroll Month End.
1185 l_pay_me date;
1186 -- Variable used to store the Payroll Month End.
1187 l_old_pay_me date;
1188 -- Variable used to hold the Calendar Year.
1189 l_cal_year date;
1190 -- Variable used to move a certain amount of rows forward.
1191 l_cur_row number;
1192 -- The number of periods in the year.
1193 l_no_periods number;
1194 -- Function name used in trace package
1195 func_name CONSTANT varchar2(50) := 'za_pay_calendars.create_za_employee_cal_years';
1196
1197 -- Cursor that holds all the periods in the generation time span.
1198 cursor get_periods is
1199 select
1200 end_date, pay_advice_date
1201 from
1202 per_time_periods
1203 where
1204 payroll_id = p_payroll_id
1205 and
1206 end_date between p_first_end_date and p_last_end_date
1207 order by
1208 end_date;
1209
1210 begin
1211
1212 ---------------------------------------------------------------------------------------
1213 -- Determine the first calendar year start in the generation period
1214 ---------------------------------------------------------------------------------------
1215 hr_utility.set_location(func_name, 1);
1216 declare
1217 l_cal_year2 date;
1218 begin
1219 -- Obtain the first period's start date
1220 select
1221 start_date
1222 into
1223 l_start_date
1224 from
1225 per_time_periods
1226 where
1227 payroll_id = p_payroll_id
1228 and
1229 end_date = p_first_end_date;
1230
1234
1231 -- Concatenate 01-JAN with the year of the start_date
1232 l_cal_year := to_date('01-JAN' ||
1233 to_char(l_start_date, 'YYYY'), 'DD-MON-YYYY');
1235 -- Determine the next year's Calendar year start
1236 l_cal_year2 := to_date('01-JAN' ||
1237 to_char(add_months(l_start_date, 12), 'YYYY'),
1238 'DD-MON-YYYY');
1239
1240 -- Determine which one of the Cal Year Starts are the closest to the start date
1241 if abs(l_cal_year2 - l_start_date) < abs(l_cal_year - l_start_date) then
1242 l_cal_year := l_cal_year2;
1243 end if;
1244
1245 -- Convert to a Calendar Year End
1246 l_cal_year := l_cal_year - 1;
1247
1248 exception
1249 when no_data_found then
1250 -- Create a dummy, since it won't be used anyway
1251 l_cal_year := to_date('01-01-1001', 'DD-MM-YYYY');
1252 end;
1253
1254 open get_periods;
1255 fetch get_periods into l_end_date, l_old_pay_me;
1256 if get_periods%notfound then
1257 -- No periods to generate Calendar Years for.
1258 null;
1259 else
1260 -- If the first period's Payroll Month End is within 14 days of the Cal Year
1261 -- End, then use that Month End as the first Employee Cal Year End
1262 if abs(l_old_pay_me-l_cal_year) < 15 then
1263 l_cal_year := l_old_pay_me;
1264
1265 -- Find the last end date in the Payroll Month End
1266 select
1267 max(end_date)
1268 into
1269 l_store_end_date
1270 from
1271 per_time_periods
1272 where
1273 payroll_id = p_payroll_id
1274 and
1275 pay_advice_date = l_old_pay_me;
1276
1277 else
1278 -- If the Payroll Month End is larger than the Cal Year End, then add a year
1279 -- to the Cal Year End
1280 if l_old_pay_me > l_cal_year then
1281 l_cal_year := add_months(l_cal_year, 12);
1282 end if;
1283
1284 loop
1285 -- Get the current period's Payroll Month End
1286 fetch get_periods into l_end_date, l_pay_me;
1287 exit when get_periods%notfound;
1288
1289 -- If the current Payroll Month End is larger than the Cal Year End, then
1290 -- decide whether this Payroll Month End or the previous one is closer to
1291 -- the Cal Year End. Use that Month End as the first Employee Cal Year End.
1292 if l_pay_me > l_cal_year then
1293
1294 -- Determine whether this Payroll Month End or the previous one is
1295 -- closer to the Calendar Year End.
1296 if abs(l_old_pay_me - l_cal_year) < abs(l_pay_me - l_cal_year) then
1297 l_cal_year := l_old_pay_me;
1298 l_store_end_date := l_old_end_date;
1299 else
1300 l_cal_year := l_old_pay_me;
1301
1302 -- Find the last end date in the Payroll Month End
1303 select
1304 max(end_date)
1305 into
1306 l_store_end_date
1307 from
1308 per_time_periods
1309 where
1310 payroll_id = p_payroll_id
1311 and
1312 pay_advice_date = l_pay_me;
1313 end if;
1314
1315 -- Exit the loop
1316 exit;
1317
1318 end if;
1319
1320 -- Store the previous Payroll Month End and End Date
1321 l_old_pay_me := l_pay_me;
1322 l_old_end_date := l_end_date;
1323 end loop;
1324 end if;
1325
1326 -- Update the first periods to the first calendar year
1327 update
1328 per_time_periods
1329 set
1330 prd_information3 = to_char(l_cal_year, 'YYYY'),
1331 prd_information_category = 'ZA'
1332 where
1333 payroll_id = p_payroll_id
1334 and
1335 end_date between p_first_end_date and l_store_end_date;
1336 end if;
1337 close get_periods;
1338
1339 ---------------------------------------------------------------------------------------
1340 -- Now default the calendar year for the rest of the periods
1341 ---------------------------------------------------------------------------------------
1342 open get_periods;
1343 fetch get_periods into l_end_date, l_pay_me;
1344 if get_periods%notfound then
1345 -- No periods to generate Calendar Years for.
1346 null;
1347 else
1348 -- Move to the next period to process
1349 while l_end_date <= l_store_end_date loop
1350 -- Get the current period's details
1351 fetch get_periods into l_end_date, l_pay_me;
1352 exit when get_periods%notfound;
1353 end loop;
1354
1355 -- If there are no more periods to process then do nothing
1356 if get_periods%notfound then
1357 -- This should never happen
1358 null;
1359 else
1360 begin
1361 -- Get the number of periods in the year
1362 select
1363 number_per_fiscal_year
1364 into
1365 l_no_periods
1366 from
1367 per_time_period_types
1368 where
1369 period_type = p_proc_period_type;
1370
1371 exception
1375 hr_utility.raise_error;
1372 -- No details exists for this period type
1373 when no_data_found then
1374 hr_utility.set_message(800, 'PAY_NO_PERIOD_DETAILS');
1376
1377 end;
1378
1379 -- Loop through all the periods until you are finished
1380 while not get_periods%notfound loop
1381
1382 -- Estimate the location of the next cal year, by adding the amount of
1383 -- payroll periods in the calendar year to the current position.
1384 l_cur_row := get_periods%rowcount + l_no_periods - 1;
1385
1386 -- Save the current end date
1387 l_old_end_date := l_end_date;
1388
1389 -- Determine the current calendar year
1390 l_cal_year := add_months(l_cal_year, 12);
1391
1392 -- Move forward by the amount of payroll periods in the calendar year
1393 while get_periods%rowcount < l_cur_row loop
1394 -- Get the current period's details
1395 fetch get_periods into l_end_date, l_pay_me;
1396 exit when get_periods%notfound;
1397 end loop;
1398
1399 -- If we got to the end of the cursor then we have to update the cal year up
1400 -- to the end of the cursor, otherwise move forward until you find the last
1401 -- period in that Payroll Month End
1402 if get_periods%notfound then
1403 -- Update the periods up to the last period end date
1404 update
1405 per_time_periods
1406 set
1407 prd_information3 = to_char(l_cal_year, 'YYYY'),
1408 prd_information_category = 'ZA'
1409 where
1410 payroll_id = p_payroll_id
1411 and
1412 end_date between l_old_end_date and p_last_end_date;
1413 else
1414 -- Find the last period in the current Payroll Month End
1415 l_old_pay_me := l_pay_me;
1416 while l_pay_me = l_old_pay_me loop
1417 fetch get_periods into l_end_date, l_pay_me;
1418 exit when get_periods%notfound;
1419 -- Save the previous end date
1420 l_store_end_date := l_end_date;
1421 end loop;
1422
1423 if get_periods%notfound then
1424 -- Update the periods up to the last period end date
1425 update
1426 per_time_periods
1427 set
1428 prd_information3 = to_char(l_cal_year, 'YYYY'),
1429 prd_information_category = 'ZA'
1430 where
1431 payroll_id = p_payroll_id
1432 and
1433 end_date between l_old_end_date and p_last_end_date;
1434 else
1435 -- Update the periods up to the previous period
1436 update
1437 per_time_periods
1438 set
1439 prd_information3 = to_char(l_cal_year, 'YYYY'),
1440 prd_information_category = 'ZA'
1441 where
1442 payroll_id = p_payroll_id
1443 and
1444 end_date between l_old_end_date and l_store_end_date;
1445 end if;
1446 end if;
1447 end loop;
1448 end if;
1449 end if;
1450 close get_periods;
1451 end create_za_employee_cal_years;
1452
1453 ---------------------------------------------------------------------------------------
1454 -- This procedure creates the payroll tax quarters for the ZA calendars, using the
1455 -- following steps:
1456 -- 1. Determine the tax year of the first period.
1457 -- 2. Default the tax quarters for this tax year by counting down from the end of
1458 -- the tax year, instead of up as explained in the following steps.
1459 -- 3. Loop through the rest of the periods.
1460 -- 4. Every time you encounter a new Payroll Month End increment the counter.
1461 -- 5. If you have counted 3 Payroll Month Ends, update the periods in between
1462 -- to the current quarter value. Increment the quarter value, if the quarter
1463 -- value is 5 then reset it to 1.
1464 -- 6. Next period.
1465 -- 7. If you did not find 3 Payroll Month Ends, but there are still some periods
1466 -- left, then update these periods to the current quarter value.
1467 -- Parameters:
1468 -- p_payroll_id - The primary key ID of the payroll.
1469 -- p_first_end_date - The first period end date in the generation time span.
1470 -- p_last_end_date - The last period end date in the generation time span.
1471 -- ASSUMPTION: The tax year and payroll month end details were already defaulted.
1472 -- NOTE: This procedure currently only handles Week and Calendar Month period types.
1473 ---------------------------------------------------------------------------------------
1474 procedure create_za_tax_quarters
1475 (
1476 p_payroll_id in number,
1477 p_first_end_date in date,
1478 p_last_end_date in date
1479 )
1480 is
1481
1482 -- Temporary variable that holds the tax year of the first period
1483 l_tax_year per_time_periods.prd_information1%TYPE;
1484 -- Variable used to store the end date of the period
1485 l_old_end_date date;
1486 -- Variable used to hold the end date of the period
1487 l_end_date date;
1488 -- Variable used to store the Payroll Month End of the period
1489 l_old_pay_me date;
1490 -- Variable used to hold the Payroll Month End of the period
1491 l_pay_me date;
1495 l_quarter number;
1492 -- Variable used to count the Payroll Month Ends
1493 l_count number;
1494 -- Variable used to count the Tax Quarters
1496 -- Variable used to check for one row left at end of generation span
1497 l_flag number := 0;
1498 -- Function name used in trace package
1499 func_name CONSTANT varchar2(50) := 'za_pay_calendars.create_za_tax_quarters';
1500
1501 -- The periods after the first tax year
1502 cursor get_periods(c_tax_year per_time_periods.prd_information1%TYPE) is
1503 select
1504 end_date, pay_advice_date
1505 from
1506 per_time_periods
1507 where
1508 payroll_id = p_payroll_id
1509 and
1510 end_date between p_first_end_date and p_last_end_date
1511 and
1512 prd_information1 > c_tax_year
1513 order by
1514 end_date;
1515
1516 -- The periods in the first tax year, in reverse order
1517 cursor get_periods2(c_tax_year per_time_periods.prd_information1%TYPE) is
1518 select
1519 end_date, pay_advice_date
1520 from
1521 per_time_periods
1522 where
1523 payroll_id = p_payroll_id
1524 and
1525 prd_information1 = c_tax_year
1526 order by
1527 end_date DESC;
1528
1529 begin
1530
1531 hr_utility.set_location(func_name, 1);
1532 begin
1533 -- Determine the tax year of the first period
1534 select
1535 prd_information1
1536 into
1537 l_tax_year
1538 from
1539 per_time_periods
1540 where
1541 payroll_id = p_payroll_id
1542 and
1543 end_date = p_first_end_date;
1544
1545 exception
1546 -- Create a dummy, since it won't be used anyway
1547 when no_data_found then
1548 l_tax_year := '1001';
1549
1550 end;
1551
1552 -- Variable used to count 3 payroll months
1553 l_count := 1;
1554 -- Variable used to default quarter
1555 l_quarter := 5;
1556 open get_periods2(l_tax_year);
1557 fetch get_periods2 into l_old_end_date, l_old_pay_me;
1558 if get_periods2%notfound then
1559 -- No periods to generate Tax Quarters for.
1560 null;
1561 else
1562 loop
1563 -- Get the current period's Payroll Month End
1564 fetch get_periods2 into l_end_date, l_pay_me;
1565 if get_periods2%notfound then
1566 if get_periods2%rowcount = 1 then
1567 -- Set l_end_date so that only 1 row will be updated
1568 l_end_date := l_old_end_date - 1;
1569 exit;
1570 else
1571 exit;
1572 end if;
1573 end if;
1574
1575 if l_pay_me < l_old_pay_me then
1576
1577 -- If we have entered a new Payroll Month End increment the counter
1578 l_count := l_count + 1;
1579
1580 -- Store the old Payroll Month end
1581 l_old_pay_me := l_pay_me;
1582
1583 -- Check whether we have reached the last Payroll Month End in the quarter
1584 if l_count = 4 then
1585
1586 -- Reset the counter
1587 l_count := 1;
1588
1589 -- Increment the quarter
1590 l_quarter := l_quarter - 1;
1591 if l_quarter = 0 then
1592 l_quarter := 4;
1593 end if;
1594
1595 -- Update the periods with the quarter
1596 update
1597 per_time_periods
1598 set
1599 prd_information2 = to_char(l_quarter),
1600 prd_information_category = 'ZA'
1601 where
1602 payroll_id = p_payroll_id
1603 and
1604 end_date between l_end_date and l_old_end_date;
1605
1606 -- Store the beginning of the next quarter
1607 l_old_end_date := l_end_date;
1608 end if;
1609
1610 end if;
1611 end loop;
1612
1613 -- Default the last few periods if needed
1614 if l_end_date < l_old_end_date then
1615
1616 -- Increment the quarter
1617 l_quarter := l_quarter - 1;
1618 if l_quarter = 0 then
1619 l_quarter := 4;
1620 end if;
1621
1622 -- Update the periods with the quarter
1623 update
1624 per_time_periods
1625 set
1626 prd_information2 = to_char(l_quarter),
1627 prd_information_category = 'ZA'
1628 where
1629 payroll_id = p_payroll_id
1630 and
1631 end_date between l_end_date and l_old_end_date;
1632
1633 end if;
1634 end if;
1635 close get_periods2;
1636
1637 -- Variable used to count 3 payroll months
1638 l_count := 1;
1639 -- Variable used to default quarter
1640 l_quarter := 0;
1641 open get_periods(l_tax_year);
1642 fetch get_periods into l_old_end_date, l_old_pay_me;
1643 if get_periods%notfound then
1644 -- No periods to generate Tax Quarters for.
1645 null;
1646 else
1647 loop
1648 -- Get the current period's Payroll Month End
1649 fetch get_periods into l_end_date, l_pay_me;
1650 exit when get_periods%notfound;
1651
1652 if l_pay_me > l_old_pay_me then
1653
1654 -- Check whether there is one row left to generate
1658
1655 if l_end_date = p_last_end_date then
1656 l_flag := 1;
1657 end if;
1659 -- If we have entered a new Payroll Month End increment the counter
1660 l_count := l_count + 1;
1661
1662 -- Store the old Payroll Month end
1663 l_old_pay_me := l_pay_me;
1664
1665 -- Check whether we have reached the last Payroll Month End in the quarter
1666 if l_count = 4 then
1667
1668 -- Reset the counter
1669 l_count := 1;
1670
1671 -- Increment the quarter
1672 l_quarter := l_quarter + 1;
1673 if l_quarter = 5 then
1674 l_quarter := 1;
1675 end if;
1676
1677 -- Update the periods with the quarter
1678 update
1679 per_time_periods
1680 set
1681 prd_information2 = to_char(l_quarter),
1682 prd_information_category = 'ZA'
1683 where
1684 payroll_id = p_payroll_id
1685 and
1686 end_date between l_old_end_date and l_end_date;
1687
1688 -- Store the beginning of the next quarter
1689 l_old_end_date := l_end_date;
1690 end if;
1691
1692 end if;
1693 end loop;
1694
1695 -- Default the last few periods if needed
1696 if (l_end_date > l_old_end_date) or l_flag = 1 then
1697
1698 -- Increment the quarter
1699 l_quarter := l_quarter + 1;
1700 if l_quarter = 5 then
1701 l_quarter := 1;
1702 end if;
1703
1704 -- Update the periods with the quarter
1705 update
1706 per_time_periods
1707 set
1708 prd_information2 = to_char(l_quarter),
1709 prd_information_category = 'ZA'
1710 where
1711 payroll_id = p_payroll_id
1712 and
1713 end_date between l_old_end_date and l_end_date;
1714
1715 end if;
1716 end if;
1717 close get_periods;
1718
1719 end create_za_tax_quarters;
1720
1721 ---------------------------------------------------------------------------------------
1722 -- This procedure creates the period numbers for the ZA calendars. This procedure
1723 -- should be called every time a change is made to the Payroll Tax Year field on the
1724 -- Additional Period Information Flexfield. It uses the following steps:
1725 -- 1. Store the first period number of the first tax year in l_period, by selecting
1726 -- it from per_time_periods.
1727 -- 2. Loop through the tax years.
1728 -- 3. Loop through the periods in the current tax year.
1729 -- 4. Update the period number of the current period to l_period.
1730 -- 5. Increment l_period.
1731 -- 4. Next period.
1732 -- 5. Reset l_period to 1.
1733 -- 6. Next tax year.
1734 -- NOTE: There is no need to call this procedure during the period generation
1735 -- process, the default period numbers should be correct.
1736 -- NOTE: The procedure should be called with the end date of the period on which
1737 -- the change occured as p_first_end_date, and the end date of the last period in
1738 -- p_last_end_date
1739 ---------------------------------------------------------------------------------------
1740 procedure create_za_period_numbers
1741 (
1742 p_payroll_id in number,
1743 p_first_end_date in date,
1744 p_last_end_date in date,
1745 p_proc_period_type in varchar2
1746 )
1747 is
1748
1749 -- Variable used to hold the current tax year
1750 l_tax_year per_time_periods.prd_information1%TYPE;
1751 -- The rowid of the period to be updated
1752 l_rowid rowid;
1753 -- The current period's number
1754 l_period number;
1755 -- The end date of the current period
1756 l_end_date date;
1757 -- Function name used in trace package
1758 func_name CONSTANT varchar2(50) := 'za_pay_calendars.create_za_period_numbers';
1759
1760 -- All tax years in the generation time span.
1761 cursor tax_years is
1762 select distinct
1763 prd_information1
1764 from
1765 per_time_periods
1766 where
1767 payroll_id = p_payroll_id
1768 and
1769 end_date between p_first_end_date and p_last_end_date
1770 order by
1771 prd_information1;
1772
1773 -- The periods in the current tax year.
1774 cursor get_periods(c_tax_year per_time_periods.prd_information1%TYPE) is
1775 select
1776 rowid, end_date
1777 from
1778 per_time_periods
1779 where
1780 payroll_id = p_payroll_id
1781 and
1782 end_date between p_first_end_date and p_last_end_date
1783 and
1784 prd_information1 = c_tax_year
1785 order by
1786 end_date;
1787
1788 begin
1789
1790 hr_utility.set_location(func_name, 1);
1791 open tax_years;
1792 fetch tax_years into l_tax_year;
1793 if tax_years%notfound then
1794 -- This should never happen
1795 null;
1796 else
1797
1798 begin
1799 -- Determine the first period number of the first tax year
1800 select
1801 period_num
1802 into
1803 l_period
1804 from
1805 per_time_periods
1806 where
1807 payroll_id = p_payroll_id
1808 and
1809 end_date = p_first_end_date;
1810
1811 exception
1812 -- Create a dummy, since it won't be used anyway.
1813 when no_data_found then
1814 l_period := 1;
1815
1816 end;
1817
1818 -- Loop through tax years
1819 loop
1820
1821 open get_periods(l_tax_year);
1822 -- Loop through periods in current tax year
1823 loop
1824
1825 -- Get the period
1826 fetch get_periods into l_rowid, l_end_date;
1827 exit when get_periods%notfound;
1828
1829 -- Update the period number of the current period
1830 update
1831 per_time_periods
1832 set
1833 period_num = l_period
1834 where
1835 rowid = l_rowid;
1836
1837 -- Update the period name of the current period
1838 update
1839 per_time_periods
1840 set
1841 period_name = to_char(l_period) || ' ' || to_char(l_end_date, 'YYYY')
1842 || ' ' || p_proc_period_type
1843 where
1844 rowid = l_rowid;
1845
1846 -- Increment the period number
1847 l_period := l_period + 1;
1848
1849 end loop;
1850 close get_periods;
1851
1852 -- Reset the period number
1853 l_period := 1;
1854
1855 -- Get the tax year
1856 fetch tax_years into l_tax_year;
1857 exit when tax_years%notfound;
1858
1859 end loop;
1860
1861 end if;
1862 close tax_years;
1863
1864 end create_za_period_numbers;
1865
1866 ---------------------------------------------------------------------------------------
1867
1868 end pay_za_calendars_pkg;