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