21854672bc2e0bef16fd938e1806649a38b0779e
[timetracker.git] / plugins / MonthlyQuota.class.php
1 <?php
2 // +----------------------------------------------------------------------+
3 // | Anuko Time Tracker
4 // +----------------------------------------------------------------------+
5 // | Copyright (c) Anuko International Ltd. (https://www.anuko.com)
6 // +----------------------------------------------------------------------+
7 // | LIBERAL FREEWARE LICENSE: This source code document may be used
8 // | by anyone for any purpose, and freely redistributed alone or in
9 // | combination with other software, provided that the license is obeyed.
10 // |
11 // | There are only two ways to violate the license:
12 // |
13 // | 1. To redistribute this code in source form, with the copyright
14 // |    notice or license removed or altered. (Distributing in compiled
15 // |    forms without embedded copyright notices is permitted).
16 // |
17 // | 2. To redistribute modified versions of this code in *any* form
18 // |    that bears insufficient indications that the modifications are
19 // |    not the work of the original author(s).
20 // |
21 // | This license applies to this document only, not any other software
22 // | that it may be combined with.
23 // |
24 // +----------------------------------------------------------------------+
25 // | Contributors:
26 // | https://www.anuko.com/time_tracker/credits.htm
27 // +----------------------------------------------------------------------+
28
29 import('ttTimeHelper');
30
31 // MontlyQuota class implements handling of work hour quotas.
32 class MonthlyQuota {
33
34   var $db;       // Database connection.
35   var $group_id; // Group id.
36   var $org_id;   // Organization id.
37
38   function __construct() {
39     $this->db = getConnection();
40     global $user;
41     $this->group_id = $user->getGroup();
42     $this->org_id = $user->org_id;
43   }
44
45   // update - deletes a quota, then inserts a new one.
46   public function update($year, $month, $minutes) {
47     $deleteSql = "delete from tt_monthly_quotas".
48       " where year = $year and month = $month and group_id = $this->group_id and org_id = $this->org_id";
49     $this->db->exec($deleteSql);
50     if ($minutes){
51       $insertSql = "insert into tt_monthly_quotas (group_id, org_id, year, month, minutes)".
52         " values ($this->group_id, $this->org_id, $year, $month, $minutes)";
53       $affected = $this->db->exec($insertSql);
54       return (!is_a($affected, 'PEAR_Error'));
55     }
56     return true;
57   }
58
59   // get - obtains either a single month quota or an array of quotas for an entire year.
60   // Month starts with 1 for January, not 0.
61   public function get($year, $month = null) {
62     if (is_null($month)){
63       return $this->getMany($year);
64     }
65     return $this->getSingle($year, $month);
66   }
67
68   // getSingle - obtains a quota for a single month.
69   private function getSingle($year, $month) {
70     $sql = "select minutes from tt_monthly_quotas".
71       " where year = $year and month = $month and group_id = $this->group_id and org_id = $this->org_id";
72     $reader = $this->db->query($sql);
73     if (is_a($reader, 'PEAR_Error')) {
74       return false;
75     }
76
77     $row = $reader->fetchRow();
78     if ($row)
79       return $row['minutes'];
80
81     // If we did not find a record, return a calculated monthly quota.
82     $numWorkdays = $this->getNumWorkdays($month, $year);
83     global $user;
84     return $numWorkdays * $user->getWorkdayMinutes();
85   }
86
87   // getUserQuota - obtains a quota for user for a single month.
88   // This quota is adjusted by quota_percent value for user.
89   public function getUserQuota($year, $month) {
90     global $user;
91
92     $minutes = $this->getSingle($year, $month);
93     $userMinutes = (int) $minutes * $user->getQuotaPercent() / 100;
94     return $userMinutes;
95   }
96
97   // getUserQuotaFrom1st - obtains a quota for user
98   // from 1st of the month up to and inclusive of $selected_date.
99   public function getUserQuotaFrom1st($selected_date) {
100     // TODO: we may need a better algorithm here. Review.
101     $monthQuotaMinutes = $this->getUserQuota($selected_date->mYear, $selected_date->mMonth);
102     $workdaysInMonth = $this->getNumWorkdays($selected_date->mMonth, $selected_date->mYear);
103
104     // Iterate from 1st up to selected date.
105     $workdaysFrom1st = 0;
106     for ($i = 1; $i <= $selected_date->mDate; $i++) {
107       $date = "$selected_date->mYear-$selected_date->mMonth-$i";
108       if (!ttTimeHelper::isWeekend($date) && !ttTimeHelper::isHoliday($date)) {
109         $workdaysFrom1st++;
110       }
111     }
112     $quotaMinutesFrom1st = (int) ($monthQuotaMinutes * $workdaysFrom1st / $workdaysInMonth);
113     return $quotaMinutesFrom1st;
114   }
115
116   // getMany - returns an array of quotas for a given year for group.
117   private function getMany($year){
118     $sql = "select month, minutes from tt_monthly_quotas".
119       " where year = $year and group_id = $this->group_id and org_id = $this->org_id";
120     $result = array();
121     $res = $this->db->query($sql);
122     if (is_a($res, 'PEAR_Error')) {
123       return false;
124     }
125
126     while ($val = $res->fetchRow()) {
127       $result[$val['month']] = $val['minutes'];
128     }
129
130     return $result;
131   }
132
133   // getNumWorkdays returns a number of work days in a given month.
134   private function getNumWorkdays($month, $year) {
135
136     $daysInMonth = cal_days_in_month(CAL_GREGORIAN, $month, $year); // Number of calendar days in month.
137
138     $workdaysInMonth = 0;
139     // Iterate through the entire month.
140     for ($i = 1; $i <= $daysInMonth; $i++) {
141       $date = "$year-$month-$i";
142       if (!ttTimeHelper::isWeekend($date) && !ttTimeHelper::isHoliday($date)) {
143         $workdaysInMonth++;
144       }
145     }
146     return $workdaysInMonth;
147   }
148
149   // isValidQuota validates a localized value as an hours quota string (in hours and minutes).
150   public function isValidQuota($value) {
151
152     if (strlen($value) == 0 || !isset($value)) return true;
153
154     if (preg_match('/^[0-9]{1,3}h?$/', $value )) { // 000 - 999
155       return true;
156     }
157
158     if (preg_match('/^[0-9]{1,3}:[0-5][0-9]$/', $value )) { // 000:00 - 999:59
159       return true;
160     }
161
162     global $user;
163     $localizedPattern = '/^([0-9]{1,3})?['.$user->getDecimalMark().'][0-9]{1,4}h?$/';
164     if (preg_match($localizedPattern, $value )) { // decimal values like 000.5, 999.25h, ... .. 999.9999h (or with comma)
165       return true;
166     }
167
168     return false;
169   }
170
171   // quotaToFloat converts a valid quota value to a float.
172   public function quotaToFloat($value) {
173
174     if (preg_match('/^[0-9]{1,3}h?$/', $value )) { // 000 - 999
175       return (float) $value;
176     }
177
178     if (preg_match('/^[0-9]{1,3}:[0-5][0-9]$/', $value )) { // 000:00 - 999:59
179       $minutes = ttTimeHelper::toMinutes($value);
180       return ($minutes / 60.0);
181     }
182
183     global $user;
184     $localizedPattern = '/^([0-9]{1,3})?['.$user->getDecimalMark().'][0-9]{1,4}h?$/';
185     if (preg_match($localizedPattern, $value )) { // decimal values like 000.5, 999.25h, ... .. 999.9999h (or with comma)
186       // Strip optional h in the end.
187       $value = trim($value, 'h');
188       if ($user->getDecimalMark() == ',')
189         $value = str_replace(',', '.', $value);
190       return (float) $value;
191     }
192
193     return null;
194   }
195 }