Adjusted monthly quotas plugin to use configurable holidays.
[timetracker.git] / plugins / MonthlyQuota.class.php
index 4e5b8dc..c8559ad 100644 (file)
 // | https://www.anuko.com/time_tracker/credits.htm
 // +----------------------------------------------------------------------+
 
+import('ttTimeHelper');
+
 // MontlyQuota class implements handling of work hour quotas.
 class MonthlyQuota {
 
   var $db;       // Database connection.
-  var $team_id;  // Team id.
+  var $group_id; // Group id.
+  var $org_id;   // Organization id.
 
-  // 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.
   function __construct() {
     $this->db = getConnection();
     global $user;
-    $this->team_id = $user->team_id;
+    $this->group_id = $user->getGroup();
+    $this->org_id = $user->org_id;
   }
 
   // update - deletes a quota, then inserts a new one.
-  public function update($year, $month, $quota) {
-    $teamId = $this->team_id;
-    $deleteSql = "DELETE FROM tt_monthly_quotas WHERE year = $year AND month = $month AND team_id = $teamId";
+  public function update($year, $month, $minutes) {
+    $deleteSql = "delete from tt_monthly_quotas".
+      " where year = $year and month = $month and group_id = $this->group_id and org_id = $this->org_id";
     $this->db->exec($deleteSql);
-    if ($quota){
-      $insertSql = "INSERT INTO tt_monthly_quotas (team_id, year, month, quota) values ($teamId, $year, $month, $quota)";
+    if ($minutes){
+      $insertSql = "insert into tt_monthly_quotas (group_id, org_id, year, month, minutes)".
+        " values ($this->group_id, $this->org_id, $year, $month, $minutes)";
       $affected = $this->db->exec($insertSql);
       return (!is_a($affected, 'PEAR_Error'));
     }
@@ -53,30 +57,18 @@ class MonthlyQuota {
   }
 
   // get - obtains either a single month quota or an array of quotas for an entire year.
-  public function get($year, $month) {
+  // Month starts with 1 for January, not 0.
+  public function get($year, $month = null) {
     if (is_null($month)){
       return $this->getMany($year);
     }
     return $this->getSingle($year, $month);
   }
 
-  // getWorkdayHours - obtains workday_hours value for a team from the database.
-  public function getWorkdayHours(){
-    $teamId = $this->team_id;
-    $sql = "SELECT workday_hours FROM tt_teams where id = $teamId";
-    $reader = $this->db->query($sql);
-    if (is_a($reader, 'PEAR_Error')) {
-      return false;
-    }
-
-    $row = $reader->fetchRow();
-    return $row['workday_hours'];
-  }
-
   // getSingle - obtains a quota for a single month.
   private function getSingle($year, $month) {
-    $teamId = $this->team_id;
-    $sql = "SELECT quota FROM tt_monthly_quotas WHERE year = $year AND month = $month AND team_id = $teamId";
+    $sql = "select minutes from tt_monthly_quotas".
+      " where year = $year and month = $month and group_id = $this->group_id and org_id = $this->org_id";
     $reader = $this->db->query($sql);
     if (is_a($reader, 'PEAR_Error')) {
       return false;
@@ -84,17 +76,47 @@ class MonthlyQuota {
 
     $row = $reader->fetchRow();
     if ($row)
-      return $row['quota'];
+      return $row['minutes'];
 
     // If we did not find a record, return a calculated monthly quota.
     $numWorkdays = $this->getNumWorkdays($month, $year);
-    return $numWorkdays * $this->getWorkdayHours();
+    global $user;
+    return $numWorkdays * $user->getWorkdayMinutes();
   }
 
-  // getMany - returns an array of quotas for a given year for team.
+  // getUserQuota - obtains a quota for user for a single month.
+  // This quota is adjusted by quota_percent value for user.
+  public function getUserQuota($year, $month) {
+    global $user;
+
+    $minutes = $this->getSingle($year, $month);
+    $userMinutes = (int) $minutes * $user->getQuotaPercent() / 100;
+    return $userMinutes;
+  }
+
+  // getUserQuotaFrom1st - obtains a quota for user
+  // from 1st of the month up to and inclusive of $selected_date.
+  public function getUserQuotaFrom1st($selected_date) {
+    // TODO: we may need a better algorithm here. Review.
+    $monthQuotaMinutes = $this->getUserQuota($selected_date->mYear, $selected_date->mMonth);
+    $workdaysInMonth = $this->getNumWorkdays($selected_date->mMonth, $selected_date->mYear);
+
+    // Iterate from 1st up to selected date.
+    $workdaysFrom1st = 0;
+    for ($i = 1; $i <= $selected_date->mDate; $i++) {
+      $date = ttTimeHelper::dateInDatabaseFormat($selected_date->mYear, $selected_date->mMonth, $i);
+      if (!ttTimeHelper::isWeekend($date) && !ttTimeHelper::isHoliday($date)) {
+        $workdaysFrom1st++;
+      }
+    }
+    $quotaMinutesFrom1st = (int) ($monthQuotaMinutes * $workdaysFrom1st / $workdaysInMonth);
+    return $quotaMinutesFrom1st;
+  }
+
+  // getMany - returns an array of quotas for a given year for group.
   private function getMany($year){
-    $teamId = $this->team_id;
-    $sql = "SELECT month, quota FROM tt_monthly_quotas WHERE year = $year AND team_id = $teamId";
+    $sql = "select month, minutes from tt_monthly_quotas".
+      " where year = $year and group_id = $this->group_id and org_id = $this->org_id";
     $result = array();
     $res = $this->db->query($sql);
     if (is_a($res, 'PEAR_Error')) {
@@ -102,7 +124,7 @@ class MonthlyQuota {
     }
 
     while ($val = $res->fetchRow()) {
-      $result[$val['month']] = $val['quota'];
+      $result[$val['month']] = $val['minutes'];
     }
 
     return $result;
@@ -117,10 +139,58 @@ class MonthlyQuota {
     // Iterate through the entire month.
     for ($i = 1; $i <= $daysInMonth; $i++) {
       $date = "$year-$month-$i";
+      $date = ttTimeHelper::dateInDatabaseFormat($year, $month, $i);
       if (!ttTimeHelper::isWeekend($date) && !ttTimeHelper::isHoliday($date)) {
         $workdaysInMonth++;
       }
     }
     return $workdaysInMonth;
   }
+
+  // isValidQuota validates a localized value as an hours quota string (in hours and minutes).
+  public function isValidQuota($value) {
+
+    if (strlen($value) == 0 || !isset($value)) return true;
+
+    if (preg_match('/^[0-9]{1,3}h?$/', $value )) { // 000 - 999
+      return true;
+    }
+
+    if (preg_match('/^[0-9]{1,3}:[0-5][0-9]$/', $value )) { // 000:00 - 999:59
+      return true;
+    }
+
+    global $user;
+    $localizedPattern = '/^([0-9]{1,3})?['.$user->getDecimalMark().'][0-9]{1,4}h?$/';
+    if (preg_match($localizedPattern, $value )) { // decimal values like 000.5, 999.25h, ... .. 999.9999h (or with comma)
+      return true;
+    }
+
+    return false;
+  }
+
+  // quotaToFloat converts a valid quota value to a float.
+  public function quotaToFloat($value) {
+
+    if (preg_match('/^[0-9]{1,3}h?$/', $value )) { // 000 - 999
+      return (float) $value;
+    }
+
+    if (preg_match('/^[0-9]{1,3}:[0-5][0-9]$/', $value )) { // 000:00 - 999:59
+      $minutes = ttTimeHelper::toMinutes($value);
+      return ($minutes / 60.0);
+    }
+
+    global $user;
+    $localizedPattern = '/^([0-9]{1,3})?['.$user->getDecimalMark().'][0-9]{1,4}h?$/';
+    if (preg_match($localizedPattern, $value )) { // decimal values like 000.5, 999.25h, ... .. 999.9999h (or with comma)
+      // Strip optional h in the end.
+      $value = trim($value, 'h');
+      if ($user->getDecimalMark() == ',')
+        $value = str_replace(',', '.', $value);
+      return (float) $value;
+    }
+
+    return null;
+  }
 }