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.
 
  11 // | There are only two ways to violate the license:
 
  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).
 
  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).
 
  21 // | This license applies to this document only, not any other software
 
  22 // | that it may be combined with.
 
  24 // +----------------------------------------------------------------------+
 
  26 // | https://www.anuko.com/time_tracker/credits.htm
 
  27 // +----------------------------------------------------------------------+
 
  29 import('ttTimeHelper');
 
  31 // MontlyQuota class implements handling of work hour quotas.
 
  34   var $db;       // Database connection.
 
  35   var $team_id;  // Team id.
 
  37   function __construct() {
 
  38     $this->db = getConnection();
 
  40     $this->team_id = $user->team_id;
 
  43   // update - deletes a quota, then inserts a new one.
 
  44   public function update($year, $month, $minutes) {
 
  45     $team_id = $this->team_id;
 
  46     $deleteSql = "DELETE FROM tt_monthly_quotas WHERE year = $year AND month = $month AND team_id = $team_id";
 
  47     $this->db->exec($deleteSql);
 
  49       $insertSql = "INSERT INTO tt_monthly_quotas (team_id, year, month, minutes) values ($team_id, $year, $month, $minutes)";
 
  50       $affected = $this->db->exec($insertSql);
 
  51       return (!is_a($affected, 'PEAR_Error'));
 
  56   // get - obtains either a single month quota or an array of quotas for an entire year.
 
  57   // Month starts with 1 for January, not 0.
 
  58   public function get($year, $month = null) {
 
  60       return $this->getMany($year);
 
  62     return $this->getSingle($year, $month);
 
  65   // getSingle - obtains a quota for a single month.
 
  66   private function getSingle($year, $month) {
 
  67     $team_id = $this->team_id;
 
  68     $sql = "SELECT minutes FROM tt_monthly_quotas WHERE year = $year AND month = $month AND team_id = $team_id";
 
  69     $reader = $this->db->query($sql);
 
  70     if (is_a($reader, 'PEAR_Error')) {
 
  74     $row = $reader->fetchRow();
 
  76       return $row['minutes'];
 
  78     // If we did not find a record, return a calculated monthly quota.
 
  79     $numWorkdays = $this->getNumWorkdays($month, $year);
 
  81     return $numWorkdays * $user->workday_minutes;
 
  84   // getMany - returns an array of quotas for a given year for team.
 
  85   private function getMany($year){
 
  86     $team_id = $this->team_id;
 
  87     $sql = "SELECT month, minutes FROM tt_monthly_quotas WHERE year = $year AND team_id = $team_id";
 
  89     $res = $this->db->query($sql);
 
  90     if (is_a($res, 'PEAR_Error')) {
 
  94     while ($val = $res->fetchRow()) {
 
  95       $result[$val['month']] = $val['minutes'];
 
 101   // getNumWorkdays returns a number of work days in a given month.
 
 102   private function getNumWorkdays($month, $year) {
 
 104     $daysInMonth = cal_days_in_month(CAL_GREGORIAN, $month, $year); // Number of calendar days in month.
 
 106     $workdaysInMonth = 0;
 
 107     // Iterate through the entire month.
 
 108     for ($i = 1; $i <= $daysInMonth; $i++) {
 
 109       $date = "$year-$month-$i";
 
 110       if (!ttTimeHelper::isWeekend($date) && !ttTimeHelper::isHoliday($date)) {
 
 114     return $workdaysInMonth;
 
 117   // isValidQuota validates a localized value as an hours quota string (in hours and minutes).
 
 118   public function isValidQuota($value) {
 
 120     if (strlen($value) == 0 || !isset($value)) return true;
 
 122     if (preg_match('/^[0-9]{1,3}h?$/', $value )) { // 000 - 999
 
 126     if (preg_match('/^[0-9]{1,3}:[0-5][0-9]$/', $value )) { // 000:00 - 999:59
 
 131     $localizedPattern = '/^([0-9]{1,3})?['.$user->decimal_mark.'][0-9]{1,4}h?$/';
 
 132     if (preg_match($localizedPattern, $value )) { // decimal values like 000.5, 999.25h, ... .. 999.9999h (or with comma)
 
 139   // quotaToFloat converts a valid quota value to a float.
 
 140   public function quotaToFloat($value) {
 
 142     if (preg_match('/^[0-9]{1,3}h?$/', $value )) { // 000 - 999
 
 143       return (float) $value;
 
 146     if (preg_match('/^[0-9]{1,3}:[0-5][0-9]$/', $value )) { // 000:00 - 999:59
 
 147       $minutes = ttTimeHelper::toMinutes($value);
 
 148       return ($minutes / 60.0);
 
 152     $localizedPattern = '/^([0-9]{1,3})?['.$user->decimal_mark.'][0-9]{1,4}h?$/';
 
 153     if (preg_match($localizedPattern, $value )) { // decimal values like 000.5, 999.25h, ... .. 999.9999h (or with comma)
 
 154       // Strip optional h in the end.
 
 155       $value = trim($value, 'h');
 
 156       if ($user->decimal_mark == ',')
 
 157         $value = str_replace(',', '.', $value);
 
 158       return (float) $value;