1 package body pay_fr_schedule_calculation as
2 /* $Header: pyfrwktm.pkb 120.1 2005/06/14 05:17:28 aparkes noship $ */
3 --
4 g_udt_name VARCHAR2(50) := 'FR_COMPANY_WORK_PATTERNS';
5 g_date_start date;
6 g_date_end date;
7 g_assignment_id number;
8 g_asg_start_date date;
9 g_asg_end_date date;
10 g_person_id number;
11 g_business_group_id number;
12 g_asg_periods number;
13 g_default_offset number := 1;
14 --
15 TYPE wp_rec_type is RECORD (start_date date
16 ,end_date date
17 ,pattern varchar2(30)
18 ,pattern_start number
19 ,pattern_index number
20 ,pattern_length number
21 );
22 TYPE wp_tab_type is TABLE of wp_rec_type INDEX BY BINARY_INTEGER;
23 --
24 TYPE days_rec_type is RECORD (hours number
25 ,protected varchar2(1)
26 ,public_holiday varchar2(1)
27 ,public_holiday_override varchar2(1)
28 ,public_holiday_in_lieu varchar2(1)
29 ,absence_non_working varchar2(1)
30 );
31 TYPE days_tab_type is TABLE of days_rec_type INDEX BY BINARY_INTEGER;
32 --
33 TYPE pattern_tab_type is TABLE of VARCHAR2(2) INDEX BY BINARY_INTEGER;
34 days_tab days_tab_type;
35 work_pattern wp_tab_type;
36 pattern pattern_tab_type;
37 --
38 procedure initialise(p_assignment_id number
39 ,p_effective_date date) is
40 --
41 cursor csr_asg is
42 select trunc(a.effective_start_date) effective_start_date
43 , trunc(a.effective_end_date) effective_end_date
44 , a.person_id
45 , a.business_group_id business_group_id
46 , scl.segment5 work_pattern
47 , nvl(scl.segment11,g_default_offset) work_pattern_start_day
48 from per_all_assignments_f a
49 , hr_soft_coding_keyflex scl
50 where a.assignment_id = p_assignment_id
51 and a.soft_coding_keyflex_id = scl.soft_coding_keyflex_id(+)
52 order by a.effective_start_date;
53 --
54 --
55 l_days_worked number;
56 l_start number;
57 l_end number;
58 l_prev_work_pattern varchar2(30);
59 l_prev_pattern_start Number;
60 l_prev_start_date date;
61 l_prev_end_date date;
62 i number;
63 l_shift_pattern_index number := 1;
64 --
65 procedure load_pattern(p_pattern in varchar2
66 ,p_effective_date date
67 ,p_pattern_index out nocopy number
68 ,p_pattern_length out nocopy number) is
69 --
70 cursor c_get_days is
71 select ci.value
72 from pay_user_tables t
73 , pay_user_columns c
74 , pay_user_rows_f r
75 , pay_user_column_instances_f ci
76 where t.user_table_name = g_udt_name
77 and c.business_group_id = g_business_group_id
78 and t.user_table_id = r.user_table_id
79 and t.user_table_id = c.user_table_id
80 and c.user_column_name = p_pattern
81 and ci.user_column_id = c.user_column_id
82 and ci.user_row_id = r.user_row_id
83 and p_effective_date
84 between r.effective_start_date and r.effective_end_date
85 and p_effective_date
86 between ci.effective_start_date and ci.effective_end_date
87 order by r.display_sequence;
88 --
89 l_length number := 0;
90 l_pattern_index number;
91 begin
92 l_pattern_index := l_shift_pattern_index;
93 --
94 for d in c_get_days loop
95 pattern(l_shift_pattern_index) := d.value;
96 l_shift_pattern_index := l_shift_pattern_index + 1;
97 l_length := l_length + 1;
98 end loop;
99 --
100 p_pattern_index := l_pattern_index;
101 p_pattern_length := l_length;
102 end load_pattern;
103 --
104 begin
105 --
106 /* if the assignment has already been initialised then exit */
107 if g_assignment_id = p_assignment_id then
108 null;
109 else
110 --
111 /* Get distinct working pattern details for the assignment */
112 l_prev_work_pattern := null;
113 l_prev_start_date := null;
114 l_prev_end_date := null;
115 l_prev_pattern_start := null;
116 i := 0;
117 pattern.delete;
118 for l_asg_rec in csr_asg loop
119 g_person_id := l_asg_rec.person_id;
120 g_business_group_id := l_asg_rec.business_group_id;
121 g_assignment_id := p_assignment_id;
122 if g_asg_start_date is Null then
123 g_asg_start_date := l_asg_rec.effective_start_date;
124 end if;
125 --Bug:2454782.Also checked the update of pattern start day.
126 if l_asg_rec.work_pattern = l_prev_work_pattern and
127 l_asg_rec.work_pattern_start_day = l_prev_pattern_start then
128 l_prev_end_date := l_asg_rec.effective_end_date;
129 else
130 if l_prev_work_pattern is not null then
131 i := i + 1;
132 work_pattern(i).start_date := l_prev_start_date;
133 work_pattern(i).end_date := l_prev_end_date;
134 work_pattern(i).pattern := l_prev_work_pattern;
135 work_pattern(i).pattern_start :=l_prev_pattern_start;
136 --
137 load_pattern(l_prev_work_pattern
138 ,p_effective_date
139 ,work_pattern(i).pattern_index
140 ,work_pattern(i).pattern_length);
141 --
142 end if;
143 --
144 l_prev_start_date := l_asg_rec.effective_start_date;
145 l_prev_end_date := l_asg_rec.effective_end_date;
146 l_prev_work_pattern := l_asg_rec.work_pattern;
147 l_prev_pattern_start := l_asg_rec.work_pattern_start_day;
148 end if;
149 end loop;
150 i := i + 1;
151 work_pattern(i).start_date := l_prev_start_date;
152 work_pattern(i).end_date := l_prev_end_date;
153 work_pattern(i).pattern_start := l_prev_pattern_start;
154 work_pattern(i).pattern := l_prev_work_pattern;
155 --
156 load_pattern(l_prev_work_pattern
157 ,p_effective_date
158 ,work_pattern(i).pattern_index
159 ,work_pattern(i).pattern_length);
160 --
161 g_asg_periods := i;
162 g_asg_end_date := l_prev_end_date;
163
164 -- UNCOMMENT THE FOLLOWING TO DEBUG IN SQL * PLUS
165 /*
166 --Displays Work Pattern info assigned to the employee
167 Dbms_Output.Put_line(rpad('Start Date',10)||' '
168 ||rpad('End Date',10)||' '
169 ||rpad('Work Pattern',20)||' '
170 ||rpad('Start',5)||' '
171 ||rpad('Index',5)||' '
172 ||rpad('Length',6));
173 Dbms_Output.Put_line(rpad('-',10,'-')||' '
174 ||rpad('-',10,'-')||' '
175 ||rpad('-',20,'-')||' '
176 ||rpad('-',5,'-')||' '
177 ||rpad('-',5,'-')||' '
178 ||rpad('-',5,'-'));
179 For i in 1..g_asg_periods Loop
180 Dbms_output.put_line(rpad(work_pattern(i).start_date,10) || ' '
181 ||rpad(work_pattern(i).end_date,10) ||' '
182 ||rpad(work_pattern(i).pattern,20) ||' '
183 ||rpad(work_pattern(i).pattern_start,5)||' '
184 ||rpad(work_pattern(i).pattern_index,5)||' '
185 ||rpad(work_pattern(i).pattern_length,5));
186 End Loop;
187 Dbms_Output.Put_line(chr(10));
188
189 --Displays Pattern Table information
190 Dbms_Output.Put_Line(rpad('index',5)||' '
191 ||rpad('Working Hours',13));
192 Dbms_Output.Put_Line(rpad('-',5,'-')||' '
193 ||rpad('-',13,'-'));
194 For i in 1..l_shift_pattern_index-1 Loop
195 Dbms_Output.Put_Line(rpad(i,5)||' '
196 ||rpad(pattern(i),13));
197 End Loop;
198 Dbms_Output.Put_line(chr(10));
199
200 */
201
202 end if;
203 end initialise;
204 ----------------------------------------------------------
205 --
206 procedure derive_schedule(p_assignment_id number
207 ,p_date_start date
208 ,p_date_end date) is
209 cursor csr_ph is
210 select to_number(to_char(holiday_date,'J')) day
211 from per_standard_holidays
212 where legislation_code = 'FR'
213 and holiday_date between p_date_start and p_date_end;
214 --
215 cursor csr_ph_overrides is
216 select to_number(to_char(date_not_taken,'J')) day
217 from per_std_holiday_absences
218 where person_id = g_person_id
219 and date_not_taken between p_date_start and p_date_end;
220 --
221 cursor csr_ph_in_lieu is
222 select to_number(to_char(actual_date_taken,'J')) day
223 from per_std_holiday_absences
224 where person_id = g_person_id
225 and actual_date_taken between p_date_start and p_date_end;
226 --
227 cursor csr_absence_non_working is
228 select to_number(to_char(
229 greatest(a.date_start,p_date_start),'J')) date_start
230 , to_number(to_char(
231 least(nvl(a.date_end,p_date_end),p_date_end),'J')) date_end
232 from per_absence_attendances a
233 , per_absence_attendance_types aat
234 , per_shared_types nw
235 where a.person_id = g_person_id
236 and a.date_start <= p_date_end
237 and nvl(a.date_end,p_date_end) >= p_date_start
238 and a.absence_attendance_type_id = aat.absence_attendance_type_id
239 and aat.absence_category = nw.system_type_cd
240 and nvl(nw.business_group_id,a.business_group_id) = a.business_group_id
241 and nw.lookup_type = 'ABSENCE_CATEGORY'
242 and nw.information1 = 'Y';
243 --
244 l_start number;
245 l_end number;
246 x number;
247 l_pattern_offset number;
248 l_pattern_start number;
249 i Number;
250 --
251 begin
252 /* In the case of Starters and Leavers it may be necessary to infer the
253 pattern wither before or after their assignment starts/ends.
254
255 This is achieved by modifying the 1st assignment period start date and the
256 last assignment period end date if they are within the p_date_start and p_date_end date range.
257 */
258 --
259
260 if p_date_start < g_asg_start_date then
261 l_pattern_offset := p_date_start - work_pattern(1).start_date;
262 work_pattern(1).pattern_start :=
263 mod(l_pattern_offset + work_pattern(1).pattern_start
264 ,work_pattern(1).pattern_length);
265 If work_pattern(1).pattern_start <= 0 Then
266 work_pattern(1).pattern_start := work_pattern(1).pattern_start + work_pattern(1).pattern_length;
267 End If;
268
269 work_pattern(1).start_date := trunc(p_date_start);
270 end if;
271 --
272 if p_date_end > g_asg_end_date then
273 work_pattern(g_asg_periods).end_date := trunc(p_date_end);
274 end if;
275 --
276 if trunc(p_date_start) = trunc(g_date_start) and
277 trunc(p_date_end) = trunc(g_date_end) and
278 p_assignment_id = g_assignment_id then
279 null;
280 else
281
282
283 days_tab.delete;
284
285 for a in 1..g_asg_periods loop
286 --
287 if work_pattern(a).end_date >= trunc(p_date_start) and
288 work_pattern(a).start_date <= trunc(p_date_end) then
289 --
290
291 l_start := to_number(to_char(greatest(p_date_start,work_pattern(a).start_date),'J'));
292 l_end := to_number(to_char(least(p_date_end,work_pattern(a).end_date),'J'));
293 /* Determine the pattern day on start date of period */
294 l_pattern_offset := greatest(trunc(p_date_start)
295 ,work_pattern(a).start_date)
296 - work_pattern(a).start_date;
297 l_pattern_start := mod(l_pattern_offset + work_pattern(a).pattern_start
298 ,work_pattern(a).pattern_length);
299 --
300 If l_pattern_start = 0 Then
301 l_pattern_start := work_pattern(a).pattern_length;
302 End If;
303
304 --
305 x := work_pattern(a).pattern_index + l_pattern_start - 1;
306 for d in l_start..l_end loop
307 if work_pattern(a).pattern_length = 0 then
308 days_tab(d).hours := 0;
309 else
310 if pattern(x) = 'P' then
311 days_tab(d).hours := 0;
312 days_tab(d).protected := 'Y';
313 else
314 days_tab(d).hours := pattern(x);
315 end if;
316 end if;
317 --
318 /* Increment index counter */
319 if x - work_pattern(a).pattern_index + 1
320 >= work_pattern(a).pattern_length then
321 x := work_pattern(a).pattern_index;
322 else
323 x := x + 1;
324 end if;
325 end loop;
326 end if;
327 end loop;
328 --
329
330 /* Load public holidays */
331 for h in csr_ph loop
332 days_tab(h.day).public_holiday := 'Y';
333 end loop;
334 --
335
336 /* Load public holiday overrides */
337 for o in csr_ph_overrides loop
338 days_tab(o.day).public_holiday_override := 'Y';
339 end loop;
340 --
341
342 /* Load public holiday taken in lieu */
343 for l in csr_ph_in_lieu loop
344 days_tab(l.day).public_holiday_in_lieu := 'Y';
345 end loop;
346 --
347
348 /* Load absences treated as non-working days */
349 for a in csr_absence_non_working loop
350 for n in a.date_start..a.date_end loop
351 days_tab(n).absence_non_working := 'Y';
352 end loop;
353 end loop;
354 --
355 l_start := to_number(to_char(p_date_start,'J'));
356 l_end := to_number(to_char(p_date_end,'J'));
357 --
358 g_date_start := trunc(p_date_start);
359 g_date_end := trunc(p_date_end);
360 end if;
361 -----------------------------------------------------
362 l_start := to_number(to_char(p_date_start,'J'));
363 l_end := to_number(to_char(p_date_end,'J'));
364
365 --Displays the main pl/sql table
366 /*
367 dbms_output.put_line(rpad('Day',21)|| ' '
368 || rpad('Hours',5)|| ' '
369 || rpad('Protected',9)|| ' '
370 || rpad('Public Holiday',14)|| ' '
371 || rpad('PH Override',11)|| ' '
372 || rpad('PH in lieu',10)|| ' '
373 || rpad('Absence NW',10));
374 dbms_output.put_line(rpad('-',21,'-')|| ' '
375 || rpad('-',5,'-')|| ' '
376 || rpad('-',9,'-')|| ' '
377 || rpad('-',14,'-')|| ' '
378 || rpad('-',11,'-')|| ' '
379 || rpad('-',10,'-')|| ' '
380 || rpad('-',10,'-'));
381
382 for d in l_start..l_end loop
383 dbms_output.put_line(rpad(to_date(d,'J'),10)|| ' '||rpad(TO_CHAR(TO_DATE(d,'J'),'DAY'),10)||' '||
384 rpad(days_tab(d).hours,5) || ' '||
385 rpad(nvl(days_tab(d).protected,' '),9) || ' '||
386 rpad(nvl(days_tab(d).public_holiday,' '),14) || ' '||
387 rpad(nvl(days_tab(d).public_holiday_override,' '),11) || ' '||
388 rpad(nvl(days_tab(d).public_holiday_in_lieu,' '),10) || ' '||
389 rpad(nvl(days_tab(d).absence_non_working,' '),10)
390 );
391 end loop;
392 */
393 end derive_schedule;
394 --
395 /* ---------------------------------------------------------
396 Function holiday_days
397
398 This function counts the number of working days between the
399 Start and End Dates passed in as parameters.
400
401 It takes into account public holidays as these should not be
402 counted as holidays if they fall on a working day. If the
403 public holiday is scheduled to be worked then it is treated
404 as working day for the purposes of counting working days.
408 Other types of absence do not affect the count of holidays.
405 A holiday taken in lieu of a public holiday is treated as
406 a non-working day for the purposes of counting working days.
407
409 ------------------------------------------------------------ */
410 function holiday_days(p_assignment_id number
411 ,p_effective_date date
412 ,p_date_start date
413 ,p_date_end date
414 ) return number is
415 l_days_worked number := 0;
416 l_start number;
417 l_end number;
418 begin
419 --
420 /* Call initialise in case this is a new person */
421 initialise(p_assignment_id
422 ,p_effective_date);
423 /* Call derive_schedule in case this is a new period */
424 derive_schedule(p_assignment_id
425 ,p_date_start
426 ,p_date_end);
427 --
428 /* count the number of days */
429 --
430 l_start := to_number(to_char(p_date_start,'J'));
431 l_end := to_number(to_char(p_date_end,'J'));
432 --
433 for d in l_start..l_end loop
434 if not(days_tab(d).hours = 0) then
435 if days_tab(d).public_holiday = 'Y' then
436 if days_tab(d).public_holiday_override = 'Y' then
437 l_days_worked := l_days_worked + 1;
438 end if;
439 else
440 if days_tab(d).public_holiday_in_lieu = 'Y' then
441 null;
442 else
443 l_days_worked := l_days_worked + 1;
444 end if;
445 end if;
446 end if;
447 end loop;
448 return l_days_worked;
449 end;
450 --
451 /* ---------------------------------------------------------
452 Function protected_days
453
454 This function takes counts the number of protected days
455 between the Start and End Dates passed in as parameters.
456
457 It takes into account public holidays as these should
458 not be counted as holidays if they fall on a working day.
459
460 Other types of absence do not affect the count of holidays.
461
462 --------------------------------------------------------- */
463 function protected_days(p_assignment_id number
464 ,p_effective_date date
465 ,p_date_start date
466 ,p_date_end date
467 ) return number is
468 l_protected_days number := 0;
469 l_start number;
470 l_end number;
471 begin
472 --
473 /* Call initialise in case this is a new person */
474 initialise(p_assignment_id
475 ,p_effective_date);
476 /* Call derive_schedule in case this is a new period */
477 derive_schedule(p_assignment_id
478 ,p_date_start
479 ,p_date_end);
480 --
481 /* count the number of protected days */
482 --
483 l_start := to_number(to_char(p_date_start,'J'));
484 l_end := to_number(to_char(p_date_end,'J'));
485 --
486 for d in l_start..l_end loop
487 if days_tab(d).protected = 'Y' then
488 if days_tab(d).public_holiday = 'Y' then
489 null;
490 else
491 l_protected_days := l_protected_days + 1;
492 end if;
493 end if;
494 end loop;
495 return l_protected_days;
496 end;
497 --
498 /* ---------------------------------------------------------
499 Function scheduled_working_days
500
501 This function will return the number of working days for the
502 assignment between the start and end dates. The effective
503 date parameter is used to determine the work pattern records
504 from the user defined table.
505
506 Only the work schedule will be used to determine the number
507 of days scheduled to be worked. Public Holidays and other
508 absences are excluded from the evaluation.
509
510 This will be used by the Sickness functionality to evaluate
511 the Sickness Deduction.
512
513 ------------------------------------------------------------ */
514 function scheduled_working_days(p_assignment_id number
515 ,p_effective_date date
516 ,p_date_start date
517 ,p_date_end date
518 ) return number is
519 l_days_worked number := 0;
520 l_start number;
521 l_end number;
522 i Number;
523 begin
524 --
525 /* Call initialise in case this is a new person */
526 initialise(p_assignment_id
527 ,p_effective_date);
528 /* Call derive_schedule in case this is a new period */
529 derive_schedule(p_assignment_id
530 ,p_date_start
531 ,p_date_end);
532 --
533 /* count the number of days */
534 --
535 l_start := to_number(to_char(p_date_start,'J'));
536 l_end := to_number(to_char(p_date_end,'J'));
537 --
538
539 i := days_tab.FIRST;
540 While i Is Not Null Loop
541 If Not(days_tab(i).hours = 0) Then
542 l_days_worked := l_days_worked + 1;
543 End If;
544
545 i := days_tab.NEXT(i);
546 End Loop;
547
548
549 return l_days_worked;
550 end;
551 --
552 /* ---------------------------------------------------------
556 assignment between the start and end dates. The effective date
553 Function scheduled_working_hours
554
555 This function will return the number of working hours for the
557 parameter is used to determine the work pattern records from
558 the user defined table.
559
560 Only the work schedule will be used to determine the number of
561 hours scheduled to be worked. Public Holidays and other absences
562 are excluded from the evaluation.
563
564 This will be used by the Proration functionality to evaluate
565 the Scheduled Hours Method.
566
567 ------------------------------------------------------------ */
568 function scheduled_working_hours(p_assignment_id number
569 ,p_effective_date date
570 ,p_date_start date
571 ,p_date_end date
572 ) return number is
573 l_hours_worked number := 0;
574 l_start number;
575 l_end number;
576 i Number;
577 begin
578 --
579 /* Call initialise in case this is a new person */
580 initialise(p_assignment_id
581 ,p_effective_date);
582 /* Call derive_schedule in case this is a new period */
583 derive_schedule(p_assignment_id
584 ,p_date_start
585 ,p_date_end);
586 --
587 /* count the number of hours */
588 --
589 l_start := to_number(to_char(p_date_start,'J'));
590 l_end := to_number(to_char(p_date_end,'J'));
591 --
592
593 i := days_tab.FIRST;
594 While i Is Not Null Loop
595 If Not(days_tab(i).hours = 0) Then
596 l_hours_worked := l_hours_worked + days_tab(i).hours;
597 End If;
598
599 i := days_tab.NEXT(i);
600 End Loop;
601
602
603
604 return l_hours_worked;
605 end;
606 --
607 /*------------------------------------------------------------
608 For the purposes of a Sickness report the last working day prior to
609 the sickness and the next working day following a particular day is required.
610
611 These functions loop through days prior to / following the P_DATE
612 determining whether the day is a working day. If it reaches the
613 P_LIMIT_DATE it stops and returns NULL. The default value
614 for the P_LIMIT_DATE is 12 months prior to / following the P_DATE.
615
616 ------------------------------------------------------*/
617 function get_next_last_working_day(p_assignment_id number
618 ,p_effective_date date
619 ,p_date date
620 ,p_limit_date date
621 ,p_next_last number
622 ) return date is
623 l_date date;
624 l_start number;
625 l_end number;
626 l_working_date date;
627 d number;
628 l_limit_date date;
629 l_act_date Date;
630 l_last_act_date Date;
631 Cursor c_start_asg is
632 Select Min(effective_start_date)
633 From per_all_assignments_f paaf
634 Where
635 paaf.assignment_id = g_assignment_id;
636
637 begin
638 initialise(p_assignment_id
639 ,p_effective_date);
640
641
642 Open c_start_asg ;
643 Fetch c_start_asg into l_act_date;
644 Close c_start_asg;
645 l_last_act_date := l_act_date;
646 l_act_date := greatest(l_act_date,p_date);
647
648 --
649 if p_limit_date is null then
650 If p_next_last = 1 then
651 l_limit_date := add_months(p_date,12*p_next_last);
652 Else
653 l_limit_date := greatest(add_months(l_act_date,12*p_next_last),l_last_act_date);
654
655 End If;
656 else
657 if p_next_last = 1 then
658 l_limit_date := greatest(p_date+1,p_limit_date);
659 else
660 l_limit_date := least(l_act_date-1,greatest(p_limit_date,l_act_date));
661 end if;
662 end if;
663 --
664 l_date := l_act_date+p_next_last;
665 while sign(l_limit_date - l_date) <> p_next_last * -1 loop
666 derive_schedule(p_assignment_id
667 ,l_date
668 ,l_date);
669 --
670 d := to_number(to_char(l_date,'J'));
671 if not(days_tab(d).hours = 0) then
672 if days_tab(d).public_holiday = 'Y' then
673 if days_tab(d).public_holiday_override = 'Y' then
674 if days_tab(d).absence_non_working = 'Y' then
675 null;
676 else
677 l_working_date := l_date;
678 exit;
679 end if;
680 end if;
681 else
682 if days_tab(d).public_holiday_in_lieu = 'Y' then
683 null;
684 else
685 if days_tab(d).absence_non_working = 'Y' then
686 null;
687 else
688 l_working_date := l_date;
689 exit;
690 end if;
691 end if;
692 end if;
693 end if;
694 l_date := l_date + p_next_last;
695 end loop;
696 --
697
698 return l_working_date;
699 end;
700 ------------------------------------------------------------
701 function get_last_working_day(p_assignment_id number
702 ,p_effective_date date
703 ,p_date date
704 ,p_limit_date date
705 ) return date is
706 begin
707 return get_next_last_working_day(p_assignment_id
708 ,p_effective_date
709 ,p_date
710 ,p_limit_date
711 ,-1
712 );
713 end;
714 --
715 function get_next_working_day(p_assignment_id number
716 ,p_effective_date date
717 ,p_date date
718 ,p_limit_date date
719 ) return date is
720 begin
721 return get_next_last_working_day(p_assignment_id => p_assignment_id
722 ,p_effective_date => p_effective_date
723 ,p_date => p_date
724 ,p_limit_date => p_limit_date
725 ,p_next_last => 1
726 );
727 end get_next_working_day;
728 --
729 end pay_fr_schedule_calculation;