DBA Data[Home] [Help]

PACKAGE BODY: APPS.PER_ACCRUAL_CALC_FUNCTIONS

Source


1 package body per_accrual_calc_functions as
2 /* $Header: peaclcal.pkb 120.6.12010000.3 2008/09/25 05:12:15 nshrikha ship $ */
3 --
4 -- Start of fix 3222662
5 Type g_entry_value is table of
6      pay_element_entry_values_f.screen_entry_value%Type
7      index by binary_integer;
8 Type g_add_subtract is table of pay_net_calculation_rules.add_or_subtract%Type
9      index by binary_integer;
10 Type g_effective_date is table of date index by binary_integer;
11 -- End of 3222662
12 --
13 g_package  varchar2(50) := '  per_accrual_calc_functions.';  -- Global package name
14 
15 /* =====================================================================
16    Name    : Calculate_Accrual
17    Purpose : Determines whether an assignment is enrolled in a plan, and
18 	     if so, executes the formula to calculate the accrual for
19 	     the plan(s).
20    ---------------------------------------------------------------------*/
21 procedure Calculate_Accrual
22 (P_Assignment_ID                  IN Number
23 ,P_Plan_ID                        IN Number
24 ,P_Payroll_ID                     IN Number
25 ,P_Business_Group_ID              IN Number
26 ,P_Accrual_formula_ID             IN Number
27 ,P_Assignment_Action_ID           IN Number default null
28 ,P_Calculation_Date               IN Date
29 ,P_Accrual_Start_Date             IN Date default null
30 ,P_Accrual_Latest_Balance         IN Number default null
31 ,P_Total_Accrued_PTO              OUT NOCOPY Number
32 ,P_Effective_Start_Date           OUT NOCOPY Date
33 ,P_Effective_End_Date             OUT NOCOPY Date
34 ,P_Accrual_End_date               OUT NOCOPY Date) is
35 --
36 /* CHECK VALIDITY OF ABOVE PARAMS */
37 
38 l_proc        varchar2(72) := g_package||'Calculate_Accrual';
39 l_inputs  ff_exec.inputs_t;
40 l_outputs ff_exec.outputs_t;
41 
42 begin
43 --
44   hr_utility.set_location('Entering '||l_proc, 5);
45   --
46   l_inputs(1).name := 'ASSIGNMENT_ID';
47   l_inputs(1).value := p_assignment_id;
48   l_inputs(2).name := 'DATE_EARNED';
49   l_inputs(2).value := fnd_date.date_to_canonical(p_calculation_date);
50   l_inputs(3).name := 'ACCRUAL_PLAN_ID';
51   l_inputs(3).value := p_plan_id;
52   l_inputs(4).name := 'BUSINESS_GROUP_ID';
53   l_inputs(4).value := p_business_group_id;
54   l_inputs(5).name := 'PAYROLL_ID';
55   l_inputs(5).value := p_payroll_id;
56   l_inputs(6).name := 'CALCULATION_DATE';
57   l_inputs(6).value := fnd_date.date_to_canonical(p_calculation_date);
58   l_inputs(7).name := 'ACCRUAL_START_DATE';
59   l_inputs(7).value := fnd_date.date_to_canonical(p_accrual_start_date);
60   l_inputs(8).name := 'ASSIGNMENT_ACTION_ID';
61   l_inputs(8).value := p_assignment_action_id;
62   l_inputs(9).name := 'ACCRUAL_LATEST_BALANCE';
63   l_inputs(9).value := p_accrual_latest_balance;
64 
65   l_outputs(1).name := 'TOTAL_ACCRUED_PTO';
66   l_outputs(2).name := 'EFFECTIVE_START_DATE';
67   l_outputs(3).name := 'EFFECTIVE_END_DATE';
68   l_outputs(4).name := 'ACCRUAL_END_DATE';
69 
70   per_formula_functions.run_formula(p_formula_id => p_accrual_formula_id,
71 				   p_calculation_date => p_calculation_date,
72 				   p_inputs => l_inputs,
73 				   p_outputs => l_outputs);
74 
75   -- Bug fix 4004565
76   -- Fast formula output p_total_accrued_pto converted to number using
77   -- fnd_number.canonical_to_number function
78 
79   p_total_accrued_pto := fnd_number.canonical_to_number(l_outputs(1).value);
80   p_effective_start_date := fnd_date.canonical_to_date(l_outputs(2).value);
81   p_effective_end_date := fnd_date.canonical_to_date(l_outputs(3).value);
82   p_accrual_end_date := fnd_date.canonical_to_date(l_outputs(4).value);
83   --
84   hr_utility.set_location('Leaving '||l_proc, 10);
85 --
86 end Calculate_Accrual;
87 
88 --
89 /* =====================================================================
90    Name    : Get_Accrual
91    Purpose :
92    Returns : Total Accrual
93    ---------------------------------------------------------------------*/
94 procedure Get_Accrual
95 (P_Assignment_ID               IN  Number
96 ,P_Calculation_Date            IN  Date
97 ,P_Plan_ID                     IN  Number
98 ,P_Business_Group_ID           IN  Number
99 ,P_Payroll_ID                  IN  Number
100 ,P_Assignment_Action_ID        IN  Number default null
101 ,P_Accrual_Start_Date          IN Date default null
102 ,P_Accrual_Latest_Balance      IN Number default null
103 ,P_Start_Date                  OUT NOCOPY Date
104 ,P_End_Date                    OUT NOCOPY Date
105 ,P_Accrual_End_Date            OUT NOCOPY Date
106 ,P_Accrual                     OUT NOCOPY number) is
107 
108 l_proc                        varchar2(72) := g_package||'Get_Accrual';
109 l_accrual_plan_rec            g_accrual_plan_rec_type;
110 l_accrual_for_plan            number := 0;
111 l_effective_start_date        date;
112 l_effective_end_date          date;
113 l_accrual_end_date            date;
114 l_enrolled_in_plan            boolean;
115 
116 begin
117 --
118   hr_utility.set_location('Entering '||l_proc, 5);
119 
120   l_accrual_plan_rec := get_accrual_plan(p_plan_id);
121 
122   l_Enrolled_In_Plan := check_assignment_enrollment(
123 				 p_assignment_id
124                                 ,l_accrual_plan_rec.accrual_plan_element_type_id
125                                 ,p_calculation_date);
126 
127   if l_enrolled_in_plan then
128   --
129     calculate_accrual(p_assignment_id => p_assignment_id,
130                     p_plan_id => p_plan_id,
131 		    p_payroll_id => p_payroll_id,
132 		    p_business_group_id => p_business_group_id,
133                     p_accrual_formula_id => l_accrual_plan_rec.accrual_formula_id,
134                     p_assignment_action_id => p_assignment_action_id,
135                     p_calculation_date => p_calculation_date,
136                     p_accrual_start_date => p_accrual_start_date,
137                     p_accrual_latest_balance => p_accrual_latest_balance,
138                     p_total_accrued_pto => l_accrual_for_plan,
139                     p_effective_start_date => l_effective_start_date,
140                     p_effective_end_date => l_effective_end_date,
141 		    p_accrual_end_date => l_accrual_end_date
142 		    );
143 
144     --
145     -- Set the return values of the out parameters
146     --
147 
148     p_start_date := l_effective_start_date;
149     p_end_date := l_effective_end_date;
150     p_accrual_end_date := l_accrual_end_date;
151     p_accrual := l_accrual_for_plan;
152 
153   --
154   else
155   --
156     p_start_date := null;
157     p_end_date := null;
158     p_accrual_end_date := null;
159     p_accrual := 0;
160   --
161   end if;
162   --
163   hr_utility.set_location('Leaving '||l_proc, 10);
164 --
165 end Get_Accrual;
166 
167 --
168 /* =====================================================================
169    Name    : Get_Accrual_Plan
170    Purpose :
171    Returns : Table of Accrual Plan Details
172    ---------------------------------------------------------------------*/
173 function Get_Accrual_Plan
174 (P_Plan_ID IN Number) RETURN g_accrual_plan_rec_type is
175 --
176   cursor c_accrual_plan_details is
177   select accrual_plan_element_type_id,
178 	 accrual_formula_id
179   from pay_accrual_plans
180   where accrual_plan_id = p_plan_id;
181 
182   l_proc               varchar2(72) := g_package||'Get_Accrual_Plan';
183   l_accrual_plan_rec   g_accrual_plan_rec_type;
184 
185 begin
186 --
187   hr_utility.set_location('Entering '||l_proc, 5);
188 
189  hr_utility.trace('plan_id = '||to_char(p_plan_id));
190   open c_accrual_plan_details;
191   fetch c_accrual_plan_details into l_accrual_plan_rec.accrual_plan_element_type_id,
192 				    l_accrual_plan_rec.accrual_formula_id;
193   close c_accrual_plan_details;
194 
195   hr_utility.set_location('Leaving '||l_proc, 10);
196 
197   return l_accrual_plan_rec;
198 --
199 end Get_Accrual_Plan;
200 
201 --
202 
203 /* =====================================================================
204    Name    : Check_Assignment_Enrollment
205    Purpose :
206    Returns : True if assignment is enrolled, otherwise false.
207    ---------------------------------------------------------------------*/
208 function Check_Assignment_Enrollment
209 (P_Assignment_ID                  IN  Number
210 ,P_Accrual_Plan_Element_Type_ID   IN  Number
211 ,P_Calculation_Date               IN  Date) return Boolean is
212 
213   cursor c_enrolled is
214   select 1
215   from pay_element_entries_f pee,
216        pay_element_links_f pel,
217        pay_element_types_f pet
218   where pel.element_link_id = pee.element_link_id
219   and   pel.element_type_id = pet.element_type_id
220   and   pee.assignment_id = p_assignment_id
221   and   pet.element_type_id = p_accrual_plan_element_type_id
222   and   p_calculation_date between pee.effective_start_date
223 			   and pee.effective_end_date;
224 
225   l_proc        varchar2(72) := g_package||'Check_Assignment_Enrollment';
226   l_enrolled    boolean;
227   l_dummy       c_enrolled%rowtype;
228 
229 begin
230 --
231   hr_utility.set_location('Entering '||l_proc, 5);
232 
233   open c_enrolled;
234   fetch c_enrolled into l_dummy;
235 
236   l_enrolled := c_enrolled%found;
237 
238   close c_enrolled;
239 
240   hr_utility.set_location('Leaving '||l_proc, 10);
241 
242   return l_enrolled;
243 --
244 end Check_Assignment_Enrollment;
245 --
246 /* =====================================================================
247    Name    : Get_Carry_Over_Values
248    Purpose :
249  Returns : Max Carry over and effective date of carry over. Used by
250      carry over process.
251  ---------------------------------------------------------------------*/
252 procedure Get_Carry_Over_Values
253 (P_CO_Formula_ID             IN   Number
254 ,P_Assignment_ID             IN   Number
255 ,P_Accrual_Plan_ID           IN   Number
256 ,P_Business_Group_ID         IN   Number
257 ,P_Payroll_ID                IN   Number
258 ,P_Calculation_Date          IN   Date
259 ,P_Session_Date              IN   Date
260 ,P_Accrual_Term              IN   Varchar2
261 ,P_Effective_Date            OUT NOCOPY  Date
262 ,P_Expiry_Date               OUT NOCOPY  Date
263 ,P_Max_Carry_Over            OUT NOCOPY  Number) is
264 
265   l_proc          varchar2(72) := g_package||'Get_Carry_Over_Values';
266   l_inputs  ff_exec.inputs_t;
267   l_outputs ff_exec.outputs_t;
268   l_p_calculation_date date; -- Added for the bug6354665
269   l_accrual_start   varchar2(1); -- Added for the bug 6354665
270 
271   -- Code change for the bug 6354665 starts here
272 
273   CURSOR csr_emp_asg_act is
274   select null from
275   per_all_assignments_f asg,
276   per_periods_of_service pps
277   where asg.assignment_id = P_Assignment_ID
278   and P_calculation_date between asg.effective_start_date
279   and asg.effective_end_date
280   and asg.period_of_service_id = pps.period_of_service_id;
281 
282   -- Code change for the bug 6354665 ends here
283 
284 begin
285 --
286   hr_utility.set_location('Entering '||l_proc, 5);
287 
288 
289 
290   l_inputs(1).name := 'ASSIGNMENT_ID';
291   l_inputs(1).value := p_assignment_id;
292   l_inputs(2).name := 'DATE_EARNED';
293   l_inputs(2).value := fnd_date.date_to_canonical(P_calculation_date);
294   l_inputs(3).name := 'ACCRUAL_PLAN_ID';
295   l_inputs(3).value := p_accrual_plan_id;
296   l_inputs(4).name := 'PAYROLL_ID';
297   l_inputs(4).value := p_payroll_id;
298   l_inputs(5).name := 'BUSINESS_GROUP_ID';
299   l_inputs(5).value := p_business_group_id;
300   l_inputs(6).name := 'CALCULATION_DATE';
301   l_inputs(6).value := fnd_date.date_to_canonical(p_calculation_date);
302   l_inputs(7).name := 'ACCRUAL_TERM';
303   l_inputs(7).value := p_accrual_term;
304 
305   l_outputs(1).name := 'MAX_CARRYOVER';
306   l_outputs(2).name := 'EFFECTIVE_DATE';
307   l_outputs(3).name := 'EXPIRY_DATE';
308   l_outputs(4).name := 'PROCESS';
309 
310   per_formula_functions.run_formula(p_formula_id => p_co_formula_id,
311                                    p_calculation_date => p_calculation_date,
312                                    p_inputs => l_inputs,
313                                    p_outputs => l_outputs);
314 
315   -- Code Change for the bug6354665 starts here
316 
317   OPEN  csr_emp_asg_act;
318   FETCH csr_emp_asg_act INTO l_accrual_start;
319   IF csr_emp_asg_act%NOTFOUND THEN
320   l_p_calculation_date := FFFUNC.ADD_DAYS(fnd_date.canonical_to_date(l_outputs(2).value),1);
321   l_inputs(2).value := fnd_date.date_to_canonical(l_p_calculation_date);
322 
323   per_formula_functions.run_formula(p_formula_id => p_co_formula_id,
324                                    p_calculation_date => p_calculation_date,
325                                    p_inputs => l_inputs,
326                                    p_outputs => l_outputs);
327   END IF;
328   CLOSE csr_emp_asg_act;
329 
330   -- Code Change for the bug6354665 ends here
331 
332   if upper(l_outputs(4).value) = 'NO' then
333   --
334     p_max_carry_over := null;
335   --
336   else
337   --
338     -- Bug fix 4004565
339     -- Fast formula output converted to number using
340     -- fnd_number.canonical_to_number function
341 
342     p_max_carry_over := fnd_number.canonical_to_number(l_outputs(1).value);
343   --
344   end if;
345 
346   p_effective_date := fnd_date.canonical_to_date(l_outputs(2).value);
347   p_expiry_date    := fnd_date.canonical_to_date(l_outputs(3).value);
348 
349   hr_utility.set_location('Leaving '||l_proc, 10);
350 --
351 end Get_Carry_Over_Values;
352 --
353 /* =====================================================================
354    Name    : Get_Absence
355    Purpose :
356    Returns : Total Absence
357    ---------------------------------------------------------------------*/
358 function Get_Absence
359 (P_Assignment_ID                  IN  Number
360 ,P_Plan_ID                        IN  Number
361 ,P_Calculation_Date               IN  Date
362 ,P_Start_Date                     IN  Date
363 ,p_absence_attendance_type_id     IN  Number default NULL
364 ,p_pto_input_value_id             IN  NUMBER default NULL) return Number is
365 
366   l_proc          varchar2(72) := g_package||'Get_Absence';
367   l_total_absence number;
368 
369    /* NOTE: This cursor has been tuned for the CBO. */
370 
371    cursor c_get_total_absence is
372    select nvl(sum(nvl(abs.absence_days, abs.absence_hours)), 0)
373    from   per_absence_attendances abs,
374           per_absence_attendance_types abt,
375           pay_net_calculation_rules    ncr
376    where  abs.absence_attendance_type_id =
377             abt.absence_attendance_type_id
378    and    abt.input_value_id = ncr.input_value_id
379    and    ((ncr.absence_attendance_type_id is not null
380            and  ncr.absence_attendance_type_id =
381                abt.absence_attendance_type_id)
382            OR (ncr.absence_attendance_type_id is null
383                and exists
384                     (select 'Y' from pay_accrual_plans
385                        where accrual_plan_id = ncr.accrual_plan_id
386                         and  ncr.input_value_id = pto_input_value_id)
387                ))
388    and    exists  (select 'Y'
389                            from   per_all_assignments_f paf
390                            where paf.assignment_id = p_assignment_id
391                              and paf.person_id = abs.person_id)
392    and    abs.date_start between p_start_date and p_calculation_date
393    and    ncr.accrual_plan_id = p_plan_id;
394 --
395    cursor c_get_abs_per_type is
396    select nvl(sum(nvl(abs.absence_days, abs.absence_hours)), 0)
397    from   per_absence_attendances abs,
398           pay_net_calculation_rules    ncr
399    where  ncr.absence_attendance_type_id = p_absence_attendance_type_id
400    and    ncr.absence_attendance_type_id = abs.absence_attendance_type_id
401    and    exists  (select 'Y'
402                            from   per_all_assignments_f paf
403                            where paf.assignment_id = p_assignment_id
404                              and paf.person_id = abs.person_id)
405    and    abs.date_start between p_start_date and p_calculation_date
406    and    ncr.accrual_plan_id = p_plan_id;
407 --
408    cursor c_get_abs_per_iv is
409    select nvl(sum(nvl(abs.absence_days, abs.absence_hours)), 0)
410    from   per_absence_attendances abs,
411           per_absence_attendance_types abt
412    where  abs.absence_attendance_type_id = abt.absence_attendance_type_id
413    and    abt.input_value_id = p_pto_input_value_id
414    and    exists ( select 1
415                      from per_all_assignments_f asg
416                     where asg.assignment_id = p_assignment_id
417                       and abs.person_id = asg.person_id
418                   )
419    and    abs.date_start between p_start_date and p_calculation_date;
420 --
421 begin
422 --
423   hr_utility.set_location('Entering '||l_proc, 5);
424   --
425   if p_absence_attendance_type_id is not null then
426     hr_utility.set_location(l_proc, 6);
427     open c_get_abs_per_type;
428     fetch c_get_abs_per_type into l_total_absence;
429     close c_get_abs_per_type;
430   elsif p_pto_input_value_id is not null then
431     hr_utility.set_location(l_proc, 7);
432     open c_get_abs_per_iv;
433     fetch c_get_abs_per_iv into l_total_absence;
434     close c_get_abs_per_iv;
435   else
436     hr_utility.set_location(l_proc, 8);
437     open c_get_total_absence;
438     fetch c_get_total_absence into l_total_absence;
439     close c_get_total_absence;
440   end if;
441   hr_utility.set_location('Leaving '||l_proc, 10);
442 
443   return nvl(l_total_absence, 0);
444 --
445 end Get_Absence;
446 
447 --
448 /* =====================================================================
449    Name    : Get_Other_Net_Contribution
450    Purpose :
451    Returns : Total contribution of other elements.
452    ---------------------------------------------------------------------*/
453 function Get_Other_Net_Contribution
454 (P_Assignment_ID               IN  Number
455 ,P_Plan_ID                     IN  Number
456 ,P_Calculation_Date            IN  Date
457 ,P_Start_Date                  IN  Date
458 ,P_Input_Value_ID              IN  Number default null) return Number is
459 
460   l_proc             varchar2(72) := g_package||'Get_Other_Net_Contribution';
461   l_contribution     number := 0;
462   -- Start of fix 3222662
463   l_limit            natural := 100; -- Limiting the bulk collect, if not limited then bulk collect
464                                      -- returns entire rows for the condition, it may affect memory
465   l_prev_collect     number  := 0;   -- Cumulative record count till previous fetch
466   l_curr_collect     number  := 0;   -- Cumulative record count including the current fetch
467   l_diff_collect     number  := 0;   -- To check that, whether the last fetch retrived any new
468                                      -- records, if not then to exit from the loop
469   g_amount_entries   g_entry_value;
470   g_add_sub_entries  g_add_subtract;
471   --
472   cursor c_get_contribution is
473   -- index hint applied for bug 4737028 to avoid bitmap conversion and usage of proper index
474   -- index PAY_ELEMENT_ENTRIES_F_N53 is now used in hint to resolve bug 5677610
475   -- index PAY_INPUT_VALUES_F_N50 is now used in hint to resolve bug 6621800
476 
477   select /*+ index(pee PAY_ELEMENT_ENTRIES_F_N53,iv PAY_INPUT_VALUES_F_N50 )*/ fnd_number.canonical_to_number(pev.screen_entry_value) amount,
478 -- Bug 4551666, bug6621800
479          ncr.add_or_subtract add_or_subtract
480     from pay_accrual_plans pap,
481          pay_net_calculation_rules ncr,
482          pay_element_entries_f pee,
483          pay_element_entry_values_f pev,
484          pay_input_values_f iv
485    where pap.accrual_plan_id  = p_plan_id
486      and pee.assignment_id    = p_assignment_id
487      and pee.element_entry_id = pev.element_entry_id
488      and pev.input_value_id   = ncr.input_value_id
489      and pap.accrual_plan_id  = ncr.accrual_plan_id
490      and ncr.input_value_id not in
491          (pap.co_input_value_id,pap.pto_input_value_id)
492      and pev.screen_entry_value is not null
493      and ((p_input_value_id is not null and p_input_value_id = ncr.input_value_id)
494            or p_input_value_id is null)
495      and pev.effective_start_date = pee.effective_start_date
496      and pev.effective_end_date = pee.effective_end_date
497      and iv.input_value_id = ncr.input_value_id
498      and p_calculation_date between iv.effective_start_date and iv.effective_end_date
499      and pee.element_type_id = iv.element_type_id
500      and exists
501         (select /*+ index(piv2 PAY_INPUT_VALUES_F_N50)*/ null  -- bug6621800
502           from pay_element_entry_values_f pev1,
503                pay_input_values_f piv2
504          where pev1.element_entry_id     = pev.element_entry_id
505            and pev1.input_value_id       = ncr.date_input_value_id
506            and pev1.effective_start_date = pev.effective_start_date
507            and pev1.effective_end_date   = pev.effective_end_date
508            and ncr.date_input_value_id   = piv2.input_value_id
509            and pee.element_type_id       = piv2.element_type_id
510            and p_calculation_date between piv2.effective_start_date
511            and piv2.effective_end_date
512            and fnd_date.canonical_to_date(decode(substr(piv2.uom, 1, 1),'D',
513                pev1.screen_entry_value, Null))
514                between p_start_date and p_calculation_date);
515   --
516 begin
517   --
518   hr_utility.set_location('Entering '||l_proc, 5);
519   --
520   open c_get_contribution;
521   --
522      loop
523      --
524          fetch c_get_contribution bulk collect into
525                g_amount_entries, g_add_sub_entries limit l_limit;
526                l_prev_collect := l_curr_collect;
527                l_curr_collect := c_get_contribution%rowcount;
528                l_diff_collect := l_curr_collect - l_prev_collect;
529             --
530             if l_diff_collect > 0 then
531                --
532                for i in g_amount_entries.first..g_amount_entries.last loop
533                   --
534                   l_contribution := l_contribution + (g_amount_entries(i) *
535                                     g_add_sub_entries(i));
536                   --
537                end loop;
538                --
539             end if;
540             --
541          -- Exiting, if the present fetch is NOT returning any new rows
542          exit when (l_diff_collect = 0);
543          --
544      --
545      end loop;
546   --
547   close c_get_contribution;
548   -- End of fix 3222662
549 /*
550   -- Bug 1570965. The below is commented out because we are interested
551   -- in displaying the negative value: it appears on the View Accruals form.
552   --
553   -- If we are dealing with a single net calculation rule,
554   -- we return the absolute value, rather than a potentially
555   -- negative one. We are only interested in the negative value
556   -- when summing all elements together, so that we get an
557   -- accurate result
558 
559   if p_input_value_id is not null then
560     l_contribution := abs(l_contribution);
561   end if;
562 */
563   --
564   hr_utility.set_location('Leaving '||l_proc, 10);
565   --
566   return nvl(l_contribution, 0);
567   --
568   --
569 end Get_Other_Net_Contribution;
570 --
571 --
572 /* =====================================================================
573    Name    : Get_Carry_Over
574    Purpose :
575    Returns : Total Carry Over amount
576    ---------------------------------------------------------------------*/
577 function Get_Carry_Over
578 (P_Assignment_ID                  IN  Number
579 ,P_Plan_ID                        IN  Number
580 ,P_Calculation_Date               IN  Date
581 ,P_Start_Date                     IN  Date) return Number is
582 
583   l_proc             varchar2(72) := g_package||'Get_Carry_Over';
584   l_carryover        number := 0;
585   l_absence          number := 0;
586   l_net_absence      number := 0;
587 -- Bug 4245674 Start
588   l_other            number := 0;
589   l_net_other        number := 0;
590   l_carry_diff       number := 0;
591 -- Bug 4245674 End
592   l_expiry_date      date;
593   l_start_date       date := p_start_date;
594   l_old_date         date;
595   -- Start of fix 3222662
596   l_count            number  := 1;
597   l_limit            natural := 100; -- Limiting the bulk collect, if not limited then bulk collect
598                                      -- returns entire rows for the condition, it may affect memory
599   l_prev_collect     number  := 0;   -- Cumulative record count till previous fetch
600   l_curr_collect     number  := 0;   -- Cumulative record count including the current fetch
601   l_diff_collect     number  := 0;   -- To check that, whether the last fetch retrived any new
602                                      -- records, if not then to exit from the loop
603   g_carry_over       g_entry_value;
604   g_expiry_date      g_effective_date;
605   g_carry_over1      g_entry_value;
606   g_expiry_date1     g_effective_date;
607   --
608   cursor c_get_carryover is
609   -- index hint applied for bug 4737028 to avoid bitmap conversion and usage of proper index
610   select /*+ index(pee PAY_ELEMENT_ENTRIES_F_N53)*/ fnd_number.canonical_to_number(nvl(pev.screen_entry_value, 0)) carryover,
611          fnd_date.canonical_to_date(nvl(pev1.screen_entry_value,
612                                         p_calculation_date)) expiry_date
613     from pay_accrual_plans pap,
614          pay_element_entry_values_f pev,
615          pay_element_entry_values_f pev1,
616          pay_input_values_f piv,
617          pay_input_values_f piv1,
618          pay_element_entries_f pee
619    where pap.accrual_plan_id   = p_plan_id
620      and pee.assignment_id     = p_assignment_id
621      and pee.element_entry_id  = pev.element_entry_id
622      and pee.element_entry_id  = pev1.element_entry_id
623      and pev.input_value_id    = pap.co_input_value_id
624      and pev1.input_value_id   = pap.co_exp_date_input_value_id
625      and pap.co_input_value_id = piv.input_value_id
626      and pap.co_exp_date_input_value_id = piv1.input_value_id
627      and p_calculation_date between piv.effective_start_date and piv.effective_end_date
628      and p_calculation_date between piv1.effective_start_date and piv1.effective_end_date
629      and pee.element_type_id = piv.element_type_id
630      and pee.element_type_id = piv1.element_type_id
631      and exists
632            (select null
633               from pay_element_entry_values_f pev2,
634                    pay_input_values_f piv2
635              where pev2.element_entry_id = pev.element_entry_id
636                and pev2.input_value_id = pap.co_date_input_value_id
637                and pev2.input_value_id = piv2.input_value_id
638                and pev2.effective_start_date = pev.effective_start_date
639                and pev2.effective_end_date = pev.effective_end_date
640                and pap.co_date_input_value_id = piv2.input_value_id
641                and pee.element_type_id = piv2.element_type_id
642                and p_calculation_date between piv2.effective_start_date
643                and piv2.effective_end_date
644                and fnd_date.canonical_to_date(decode(substr(piv2.uom, 1, 1),'D',
645                      pev2.screen_entry_value, Null)) <=
646                      fnd_date.canonical_to_date(nvl(pev1.screen_entry_value,p_calculation_date))
647                and fnd_date.canonical_to_date(decode(substr(piv2.uom, 1, 1),'D',
648                    pev2.screen_entry_value, Null))
649                    between p_start_date and p_calculation_date)
650    order by expiry_date;
651   --
652 begin
653   --
654   hr_utility.set_location('Entering '||l_proc, 5);
655   --
656   -- Getting entire records into a PL/SQL table
657   --
658   open c_get_carryover;
659      --
660      loop
661      --
662         fetch c_get_carryover bulk collect into g_carry_over1, g_expiry_date1 limit l_limit;
663            --
664            l_prev_collect := l_curr_collect;
665            l_curr_collect := c_get_carryover%rowcount;
666            l_diff_collect := l_curr_collect - l_prev_collect;
667            --
668            if l_diff_collect > 0 then
669               --
670               for i in g_carry_over1.first..g_carry_over1.last loop
671                  -- Setting the index
672                  l_count := g_carry_over.count + 1;
673                  -- Keeping the cumulated records to a seperate PL/SQL tables
674                  g_carry_over(l_count) := g_carry_over1(i);
675                  g_expiry_date(l_count) := g_expiry_date1(i);
676               end loop;
677               --
678            end if;
679            --
680         -- Exiting, if the present fetch is NOT returning any new rows
681         exit when (l_diff_collect = 0);
682      --
683      end loop;
684   --
685   close c_get_carryover;
686   --
687   --
688 -- Bug 4245674 Start
689 -- Desc : Modified this function to incorporate the Net Calculation rules
690 --        while calculating the net calculation rules.
691 --        Added comments throughout the fuction to explain each and every
692 --        actions.
693   l_old_date := p_start_date - 1;
694   --
695   if g_carry_over.count > 0 then
696      --
697 --  Because although you normally only have one carry over element entry,
698 --  users can manually add additional carry over entries in by hand.
699      for i in g_carry_over.first..g_carry_over.last loop
700         --
701 --  If you have more than one carry over entry and its expiry date is
702 --  later than the previous element entry, you need to sum any additional
703 --  absences and other contribution between the the first expiry period
704 --  and the second and add them to the total.
705         if g_expiry_date(i) > l_old_date then
706            --
707            l_start_date := l_old_date + 1;
708            --
709            l_absence := get_absence(
710                         p_assignment_id => p_assignment_id,
711                         p_plan_id => p_plan_id,
712                         p_start_date => l_start_date,
713                         p_calculation_date => g_expiry_date(i));
714 
715            l_absence := l_absence + l_net_absence;
716 
717            l_other := get_other_net_contribution(
718 			     p_assignment_id => p_assignment_id,
719                              p_plan_id => p_plan_id,
720                              p_start_date => l_start_date,
721                              p_calculation_date => g_expiry_date(i));
722            l_other := l_other + l_net_other;
723            --
724         else
725            --
726            l_absence := l_net_absence;
727            l_other := l_net_other;
728            --
729         end if;
730         --
731         if p_calculation_date <= g_expiry_date(i) then
732            --
733            l_net_absence := l_absence;
734            l_carryover := l_carryover + g_carry_over(i);
735            l_net_other := l_other;
736            --
737 --  When the carryover has already expired, we showing the carryover amount
738 --  to be the amount of absences and other contribution - ie the amount
739 --  not forfeited.
740         else
741            --
742 --  This is for absence to be calculated.
743            if g_carry_over(i) <= l_absence then
744               --
745               l_net_absence := l_absence - g_carry_over(i);
746               l_carryover := l_carryover + g_carry_over(i);
747               l_carry_diff := 0;
748               --
749            else
750               --
751               l_net_absence := 0;
752               l_carryover := l_carryover + l_absence;
753               l_carry_diff := g_carry_over(i) - l_absence;
754               --
755            end if;
756            --
757 --  This is for Net Calculation rules to be calculated.
758            if l_other > 0 then
759               l_net_other := l_other;
760            else
761 
762               if l_carry_diff <= abs(l_other) then
763 --  Remaining other is add into net_other.
764                  l_net_other := l_other + l_carry_diff;
765 --  Adding the carryover in the absence and  remaining carryover if the
766 --  net carryover calculated after absence deduction is less than the
767 --  net carryover.
768                  l_carryover := l_carryover + l_carry_diff;
769 
770               else
771 --  Utilized all the other. If some other remains then add to net
772                  l_net_other := 0;
773 --  Case 1: when the other element is -ve(Subtract), Adding the carryover
774 --  and -ve of other if the net carryover is greater than other.
775 --
776 --  Case 2: when the other element is +ve (Add), If the carryover due to
777 --  absence is less than the other then what  needs to be done.
778 --  If the carryover due to absence is more than the other then net
779 --  carryover is carryover_absence - other
780                  l_carryover := l_carryover - l_other;
781               end if;
782 
783            end if;
784 
785         end if;
786         --
787         l_old_date := g_expiry_date(i);
788         --
789      end loop;
790      --
791   end if;
792   --
793   hr_utility.set_location('Leaving '||l_proc, 10);
794   --
795   return l_carryover;
796   --
797   -- End of fix 3222662
798   -- Bug 4245674 Ends.
799 --
800 end Get_Carry_Over;
801 --
802 --
803 /* =====================================================================
804    Name    : Get_Net_Accrual
805    Purpose :
806    Returns : Total Accrued entitlement
807    ---------------------------------------------------------------------*/
808 procedure Get_Net_Accrual
809 (P_Assignment_ID                  IN  Number
810 ,P_Plan_ID                        IN  Number
811 ,P_Payroll_ID                     IN  Number
812 ,P_Business_Group_ID              IN  Number
813 ,P_Assignment_Action_ID           IN  Number default -1
814 ,P_Calculation_Date               IN  Date
815 ,P_Accrual_Start_Date             IN  Date default null
816 ,P_Accrual_Latest_Balance         IN Number default null
817 ,P_Calling_Point                  IN Varchar2 default 'FRM'
818 ,P_Start_Date                     OUT NOCOPY Date
819 ,P_End_Date                       OUT NOCOPY Date
820 ,P_Accrual_End_Date               OUT NOCOPY Date
821 ,P_Accrual                        OUT NOCOPY Number
822 ,P_Net_Entitlement                OUT NOCOPY Number) is
823 
824   l_proc        varchar2(72) := g_package||'Get_Net_Accrual';
825   l_absence     number;
826   l_accrual     number;
827   l_other       number;
828   l_carryover   number;
829   l_start_date  date;
830   l_end_date    date;
831   l_accrual_end_date date;
832   l_defined_balance_id number;
833 
834   cursor c_get_balance is
835   select defined_balance_id
836   from pay_accrual_plans
837   where accrual_plan_id = p_plan_id;
838 
839 begin
840 --
841   hr_utility.set_location('Entering '||l_proc, 5);
842 
843   --
844   -- Pipe the parameters for ease of debugging.
845   --
846   hr_utility.trace(' ');
847   hr_utility.trace(' --------------------------------'||
848                    '---------------------------------');
849   hr_utility.trace(' ENTERING '||upper(l_proc));
850   hr_utility.trace(' for assignment '||to_char(p_assignment_id));
851   hr_utility.trace(' --------------------------------'||
852                    '+--------------------------------');
853   hr_utility.trace('  p_assignment_id                  '||
854                       to_char(p_assignment_id));
855   hr_utility.trace('  p_plan_id                        '||
856                       to_char(p_plan_id));
857   hr_utility.trace('  p_payroll_id                     '||
858                       to_char(p_payroll_id));
859   hr_utility.trace('  p_business_group_id              '||
860                       to_char(p_business_group_id));
861   hr_utility.trace('  p_assignment_action_id           '||
862                       to_char(p_assignment_action_id));
863   hr_utility.trace('  p_calculation_date               '||
864                       to_char(p_calculation_date));
865   hr_utility.trace('  p_accrual_start_date             '||
866                       to_char(p_accrual_start_date));
867   hr_utility.trace('  p_accrual_latest_balance         '||
868                       to_char(p_accrual_latest_balance));
869   hr_utility.trace('  p_calling_point                  '||
870                       p_calling_point);
871   hr_utility.trace(' --------------------------------'||
872                    '---------------------------------');
873   hr_utility.trace(' ');
874 
875   open c_get_balance;
876   fetch c_get_balance into l_defined_balance_id;
877   close c_get_balance;
878 
879   if p_calling_point = 'BP' and
880      l_defined_balance_id is not null and
881      p_assignment_action_id <> -1 then
882   --
883     /* Procedure called from batch process, so
884        get latest balance. */
885 
886     p_net_entitlement := pay_balance_pkg.get_value(
887                             p_defined_balance_id => l_defined_balance_id
888                            ,p_assignment_action_id => p_assignment_action_id
889                             );
890   --
891   else
892   --
893 
894     get_accrual(p_assignment_id => p_assignment_id,
895                 p_plan_id => p_plan_id,
896                 p_calculation_date => p_calculation_date,
897                 p_business_group_id => p_business_group_id,
898 	        p_payroll_id => p_payroll_id,
899                 p_assignment_action_id => p_assignment_action_id,
900                 p_accrual_start_date => p_accrual_start_date,
901                 p_accrual_latest_balance => p_accrual_latest_balance,
902                 p_start_date => l_start_date,
903                 p_end_date => l_end_date,
904 	        p_accrual_end_date => l_accrual_end_date,
905                 p_accrual => l_accrual);
906 
907     l_absence := get_absence(p_assignment_id => p_assignment_id,
908                              p_plan_id => p_plan_id,
909 			     p_start_date => l_start_date,
910 			     p_calculation_date => l_end_date);
911 
912     l_other := get_other_net_contribution(
913 			     p_assignment_id => p_assignment_id,
914                              p_plan_id => p_plan_id,
915                              p_start_date => l_start_date,
916                              p_calculation_date => l_end_date
917 			     );
918 
919     l_carryover := get_carry_over(
920                              p_assignment_id => p_assignment_id,
921                              p_plan_id => p_plan_id,
922                              p_start_date => l_start_date,
923                              p_calculation_date => l_end_date);
924 
925     --
926     -- Set up values in the return parameters.
927     --
928     p_net_entitlement := l_accrual - l_absence + l_other + l_carryover;
929     p_accrual := l_accrual;
930     p_start_date := l_start_date;
931     p_end_date := l_end_date;
932     p_accrual_end_date := l_accrual_end_date;
933   --
934   end if;
935 
936   --
937   -- Pipe the parameters for ease of debugging.
938   --
939   hr_utility.trace(' ');
940   hr_utility.trace(' --------------------------------'||
941                    '---------------------------------');
942   hr_utility.trace(' LEAVING '||upper(l_proc));
943   hr_utility.trace(' --------------------------------'||
944                    '+--------------------------------');
945   hr_utility.trace('  p_start_date                     '||
946                       to_char(p_start_date));
947   hr_utility.trace('  p_end_date                       '||
948                       to_char(p_end_date));
949   hr_utility.trace('  p_accrual_end_date               '||
950                       to_char(p_accrual_end_date));
951   hr_utility.trace('  p_accrual                        '||
952                       to_char(p_accrual));
953   hr_utility.trace('  p_net_entitlement                '||
954                       to_char(p_net_entitlement));
955   hr_utility.trace(' --------------------------------'||
956                    '---------------------------------');
957   hr_utility.trace(' ');
958 
959 --
960 end Get_Net_Accrual;
961 --
962 /* =====================================================================
963    Name    : get_asg_inactive_days
964    Purpose : Gets the number of days in a period where the assignment
965              status is not 'Active'.
966    Returns : Number of inactive days in the period.
967    ---------------------------------------------------------------------*/
968 FUNCTION get_asg_inactive_days
969   (p_assignment_id      IN    NUMBER,
970    p_period_sd          IN    DATE,
971    p_period_ed          IN    DATE) RETURN NUMBER IS
972 
973 CURSOR csr_period_asg_status IS
974   SELECT asg.effective_start_date,
975          asg.effective_end_date,
976          ast.per_system_status
977   FROM   per_all_assignments_f asg,
978          per_assignment_status_types ast
979   WHERE  asg.assignment_id = p_assignment_id
980   AND  ((asg.effective_start_date BETWEEN p_period_sd AND p_period_ed
981   OR     asg.effective_end_date BETWEEN p_period_sd AND p_period_ed)
982   OR    (p_period_sd BETWEEN asg.effective_start_date AND asg.effective_end_date))
983   AND    asg.assignment_status_type_id = ast.assignment_status_type_id
984   AND    ast.per_system_status <> 'ACTIVE_ASSIGN';
985 
986   l_proc               VARCHAR2(72) := g_package||'get_loa_days';
987   l_assignment_sd      DATE;
988   l_assignment_ed      DATE;
989   l_assignment_status  per_assignment_status_types.per_system_status%TYPE;
990   l_asg_inactive_days  NUMBER       := 0;
991 
992 BEGIN
993 --
994   hr_utility.set_location('Entering '||l_proc, 10);
995 
996   -- Loop each inactive assignment record for the period and count the inactive days
997   OPEN csr_period_asg_status;
998   LOOP
999     FETCH csr_period_asg_status INTO
1000       l_assignment_sd,
1001       l_assignment_ed,
1002       l_assignment_status;
1003     EXIT WHEN csr_period_asg_status%NOTFOUND;
1004     -- Count inactive days
1005     l_asg_inactive_days := l_asg_inactive_days + get_working_days(
1006       GREATEST(l_assignment_sd, p_period_sd),
1007       LEAST(l_assignment_ed, p_period_ed));
1008   END LOOP;
1009  CLOSE csr_period_asg_status;
1010 
1011   hr_utility.set_location('Leaving '||l_proc, 20);
1012 
1013   RETURN l_asg_inactive_days;
1014 
1015 END get_asg_inactive_days;
1016 --
1017 /* =====================================================================
1018    Name    : get_working_days
1019    Purpose : Gets the number of working days in a given period.
1020    Returns : Number of working days in the period.
1021    ---------------------------------------------------------------------*/
1022 FUNCTION get_working_days
1023   (p_start_date  IN    DATE,
1024    p_end_date    IN    DATE) RETURN NUMBER IS
1025 
1026   l_proc          VARCHAR2(72) := g_package||'get_working_days';
1027   l_working_days  NUMBER       := 0;
1028   l_curr_date     DATE         := NULL;
1029   l_curr_day      NUMBER       := 0;
1030   l_ref_day       NUMBER;
1031   l_adj_day       NUMBER;
1032   l_diff_days     NUMBER;
1033 
1034 BEGIN
1035 --
1036   hr_utility.set_location('Entering '||l_proc, 10);
1037 
1038   -- Check for a valid range
1039   IF p_start_date > p_end_date THEN
1040     RETURN l_working_days;
1041   END IF;
1042 
1043   -- Select the day of week for a date known to be a Saturday.
1044   -- On an instance with NLS_TERRITORY set to AMERICAN, this will
1045   -- return 7; with NLS_TERRITORY set to POLAND, it will return 6.
1046   -- Start of 3222662
1047   l_ref_day := to_number(to_char(to_date('01/01/2000', 'dd/mm/yyyy'), 'D'));
1048   -- End of 3222662
1049 
1050   hr_utility.trace('l_ref_day = '||to_char(l_ref_day));
1051 
1052   -- A non-zero difference here indicates the week does not begin
1053   -- on Sunday and provides the adjustment we must consider when
1054   -- determining whether or not a day is a work day.
1055   l_diff_days := 7 - l_ref_day;
1056 
1057   hr_utility.trace('l_diff_days = '||to_char(l_diff_days));
1058 
1059   -- Loop each day in period and count working days
1060 
1061   l_curr_date := p_start_date;
1062 
1063   LOOP
1064 
1065     l_curr_day := to_number(to_char(l_curr_date, 'D'));
1066 
1067     hr_utility.trace('l_curr_day = '||to_char(l_curr_day));
1068 
1069     -- Find the adjusted day of week
1070     -- Start of 3222662
1071     if mod(l_curr_day+l_diff_days,7) = 0 then
1072        l_adj_day := 7;
1073     else
1074        l_adj_day := mod(l_curr_day+l_diff_days,7);
1075     end if;
1076     -- End of 3222662
1077 
1078     hr_utility.trace('l_adj_day = '||to_char(l_adj_day));
1079 
1080     IF l_adj_day > 1 AND l_adj_day < 7 THEN
1081       l_working_days := l_working_days + 1;
1082     END IF;
1083     l_curr_date := l_curr_date + 1;
1084   EXIT WHEN l_curr_date > p_end_date;
1085   END LOOP;
1086 
1087   hr_utility.set_location('Leaving '||l_proc, 20);
1088 
1089   RETURN l_working_days;
1090 
1091 END get_working_days;
1092 --
1093 --
1094 end per_accrual_calc_functions;