Merges an updated upstream from main.
[timetracker.git] / plugins / MonthlyQuota.class.php
1 <?php
2
3 class MonthlyQuota {
4     
5     var $db;
6     var $holidays;
7     // Old style constructors are DEPRECATED in PHP 7.0, and will be removed in a future version. You should always use __construct() in new code.
8     function __construct() {
9         $this->db = getConnection();
10         $i18n = $GLOBALS['I18N'];
11         $this->holidays = $i18n->holidays;
12     }
13     
14     public function update($year, $month, $quota) {
15         $deleteSql = "DELETE FROM tt_monthly_quota WHERE year = $year AND month = $month";
16         $this->db->exec($deleteSql);
17         $insertSql = "INSERT INTO tt_monthly_quota (year, month, quota) values ($year, $month, $quota)";
18         $affected = $this->db->exec($insertSql);
19         return (!is_a($affected, 'PEAR_Error'));
20     }
21         
22     public function get($year, $month) {
23         
24         if (is_null($month)){
25             return $this->getMany($year);
26         }
27         
28         return $this->getSingle($year, $month);
29     }
30     
31     private function getSingle($year, $month) {
32         
33         $sql = "SELECT quota FROM tt_monthly_quota WHERE year = $year AND month = $month";
34         $reader = $this->db->query($sql);
35         if (is_a($reader, 'PEAR_Error')) {
36             return false;
37         }
38         
39         $row = $reader->fetchRow();
40         
41         // if we don't find a record, return calculated monthly quota
42         if (is_null($row)){
43             
44             $holidaysWithYear = array();
45             foreach ($this->holidays as $day) {
46                 $parts = explode("/", $day);
47                 $holiday = "$year-$parts[0]-$parts[1]";
48                 array_push($holidaysWithYear, $holiday);
49             }
50             
51             $daysInMonth = cal_days_in_month(CAL_GREGORIAN, $month, $year);
52             return $this->getWorkingDays("$year-$month-01", "$year-$month-$daysInMonth", $holidaysWithYear) * 8;
53         }
54         
55         return $row["quota"];  
56     }
57     
58     private function getMany($year){
59         $sql = "SELECT year, month, quota FROM tt_monthly_quota WHERE year = $year";
60         $result = array();
61         $res = $this->db->query($sql);
62         if (is_a($res, 'PEAR_Error')) {
63             return false;
64         }
65         
66         while ($val = $res->fetchRow()) {
67             $result[$val["month"]] = $val["quota"];
68             // $result[] = $val;
69         }        
70         
71         return $result;
72     }
73     
74     //The function returns the no. of business days between two dates and it skips the holidays
75     private function getWorkingDays($startDate, $endDate, $holidays) {
76         // do strtotime calculations just once
77         $endDate = strtotime($endDate);
78         $startDate = strtotime($startDate);
79
80         //The total number of days between the two dates. We compute the no. of seconds and divide it to 60*60*24
81         //We add one to inlude both dates in the interval.
82         $days = ($endDate - $startDate) / 86400 + 1;
83
84         $noOfFullWeeks = floor($days / 7);
85         $noOfRemainingDays = fmod($days, 7);
86
87         //It will return 1 if it's Monday,.. ,7 for Sunday
88         $firstDayofWeek = date("N", $startDate);
89         $lastDayofWeek = date("N", $endDate);
90
91         //---->The two can be equal in leap years when february has 29 days, the equal sign is added here
92         //In the first case the whole interval is within a week, in the second case the interval falls in two weeks.
93         if ($firstDayofWeek <= $lastDayofWeek) {
94             if ($firstDayofWeek <= 6 && 6 <= $lastDayofWeek) {
95                 $noOfRemainingDays--;                
96             }
97             
98             if ($firstDayofWeek <= 7 && 7 <= $lastDayofWeek) {
99                 $noOfRemainingDays--;
100             }
101         }
102         else {
103             // (edit by Tokes to fix an edge case where the start day was a Sunday
104             // and the end day was NOT a Saturday)
105
106             // the day of the week for start is later than the day of the week for end
107             if ($firstDayofWeek == 7) {
108                 // if the start date is a Sunday, then we definitely subtract 1 day
109                 $noOfRemainingDays--;
110
111                 if ($lastDayofWeek == 6) {
112                     // if the end date is a Saturday, then we subtract another day
113                     $noOfRemainingDays--;
114                 }
115             }
116             else {
117                 // the start date was a Saturday (or earlier), and the end date was (Mon..Fri)
118                 // so we skip an entire weekend and subtract 2 days
119                 $noOfRemainingDays -= 2;
120             }
121         }
122
123         //T he no. of business days is: (number of weeks between the two dates) * (5 working days) + the remainder
124         // ---->february in none leap years gave a remainder of 0 but still calculated weekends between first and last day, this is one way to fix it
125         $workingDays = $noOfFullWeeks * 5;
126         if ($noOfRemainingDays > 0 ) {
127             $workingDays += $noOfRemainingDays;
128         }
129
130         // We subtract the holidays
131         foreach($holidays as $holiday){
132             $timeStamp = strtotime($holiday);
133             // If the holiday doesn't fall in weekend
134             // TODO: add handling for countries where they move non working day to first working day if holiday is on weekends
135             if ($startDate <= $timeStamp && $timeStamp <= $endDate && date("N", $timeStamp) != 6 && date("N", $timeStamp ) != 7)
136                 $workingDays--;
137         }
138
139         return $workingDays;
140     }
141 }