diff --git a/htdocs/core/lib/date.lib.php b/htdocs/core/lib/date.lib.php
index 2694747f52d..b604ea831b1 100644
--- a/htdocs/core/lib/date.lib.php
+++ b/htdocs/core/lib/date.lib.php
@@ -963,7 +963,7 @@ function monthArray($outputlangs, $short = 0)
}
/**
* Return array of week numbers.
-
+
*
* @param int $month Month number
* @param int $year Year number
@@ -981,7 +981,7 @@ function getWeekNumbersOfMonth($month, $year) {
}
/**
* Return array of first day of weeks.
-
+
*
* @param array $TWeek array of week numbers
* @param int $year Year number
@@ -997,7 +997,7 @@ function getFirstDayOfEachWeek($TWeek, $year) {
}
/**
* Return array of last day of weeks.
-
+
*
* @param array $TWeek array of week numbers
* @param int $year Year number
@@ -1012,14 +1012,13 @@ function getLastDayOfEachWeek($TWeek, $year) {
}
/**
* Return week number.
-
+
*
* @param int $day Day number
* @param int $month Month number
* @param int $year Year number
* @return int Week number
*/
-
function getWeekNumber($day, $month, $year) {
$date = new DateTime($year.'-'.$month.'-'.$day);
$week = $date->format("W");
diff --git a/htdocs/core/lib/project.lib.php b/htdocs/core/lib/project.lib.php
index 9842f5357b4..e2f08e7c092 100644
--- a/htdocs/core/lib/project.lib.php
+++ b/htdocs/core/lib/project.lib.php
@@ -263,6 +263,14 @@ function project_timesheet_prepare_head($mode, $fuser = null)
$param .= ($mode ? '&mode='.$mode : '');
if (is_object($fuser) && $fuser->id > 0 && $fuser->id != $user->id) $param .= '&search_usertoprocessid='.$fuser->id;
+ if (empty($conf->global->PROJECT_DISABLE_TIMESHEET_PERMONTH))
+ {
+ $head[$h][0] = DOL_URL_ROOT."/projet/activity/permonth.php".($param?'?'.$param:'');
+ $head[$h][1] = $langs->trans("InputPerMonth");
+ $head[$h][2] = 'inputpermonth';
+ $h++;
+ }
+
if (empty($conf->global->PROJECT_DISABLE_TIMESHEET_PERWEEK))
{
$head[$h][0] = DOL_URL_ROOT."/projet/activity/perweek.php".($param ? '?'.$param : '');
@@ -1141,7 +1149,7 @@ function projectLinesPerDay(&$inc, $parent, $fuser, $lines, &$level, &$projectsr
if ($oldprojectforbreak != -1) $oldprojectforbreak = $projectstatic->id;
- print '
'."\n";
+ print '
'."\n";
// User
/*
@@ -1520,7 +1528,7 @@ function projectLinesPerWeek(&$inc, $firstdaytoshow, $fuser, $parent, $lines, &$
if ($oldprojectforbreak != -1) $oldprojectforbreak = $projectstatic->id;
- print '
'."\n";
+ print '
'."\n";
// User
/*
@@ -1704,6 +1712,271 @@ function projectLinesPerWeek(&$inc, $firstdaytoshow, $fuser, $parent, $lines, &$
return $totalforeachday;
}
+/**
+ * Output a task line into a perday intput mode
+ *
+ * @param string $inc Line output identificator (start to 0, then increased by recursive call)
+ * @param int $firstdaytoshow First day to show
+ * @param User|null $fuser Restrict list to user if defined
+ * @param string $parent Id of parent task to show (0 to show all)
+ * @param Task[] $lines Array of lines (list of tasks but we will show only if we have a specific role on task)
+ * @param int $level Level (start to 0, then increased/decrease by recursive call)
+ * @param string $projectsrole Array of roles user has on project
+ * @param string $tasksrole Array of roles user has on task
+ * @param string $mine Show only task lines I am assigned to
+ * @param int $restricteditformytask 0=No restriction, 1=Enable add time only if task is a task i am affected to
+ * @param array $isavailable Array with data that say if user is available for several days for morning and afternoon
+ * @param int $oldprojectforbreak Old project id of last project break
+ * @return array Array with time spent for $fuser for each day of week on tasks in $lines and substasks
+ */
+function projectLinesPerMonth(&$inc, $firstdaytoshow, $fuser, $parent, $lines, &$level, &$projectsrole, &$tasksrole, $mine, $restricteditformytask, &$isavailable, $oldprojectforbreak=0, $TWeek=array())
+{
+ global $conf, $db, $user, $bc, $langs;
+ global $form, $formother, $projectstatic, $taskstatic, $thirdpartystatic;
+
+ $numlines=count($lines);
+
+ $lastprojectid=0;
+ $workloadforid=array();
+ $totalforeachweek=array();
+ $lineswithoutlevel0=array();
+
+ // Create a smaller array with sublevels only to be used later. This increase dramatically performances.
+ if ($parent == 0) // Always and only if at first level
+ {
+ for ($i = 0 ; $i < $numlines ; $i++)
+ {
+ if ($lines[$i]->fk_task_parent) $lineswithoutlevel0[]=$lines[$i];
+ }
+ }
+
+ //dol_syslog('projectLinesPerWeek inc='.$inc.' firstdaytoshow='.$firstdaytoshow.' task parent id='.$parent.' level='.$level." count(lines)=".$numlines." count(lineswithoutlevel0)=".count($lineswithoutlevel0));
+
+ if (empty($oldprojectforbreak))
+ {
+ $oldprojectforbreak = (empty($conf->global->PROJECT_TIMESHEET_DISABLEBREAK_ON_PROJECT)?0:-1); // 0 = start break, -1 = never break
+ }
+
+ for ($i = 0 ; $i < $numlines ; $i++)
+ {
+ if ($parent == 0) $level = 0;
+
+ if ($lines[$i]->fk_task_parent == $parent)
+ {
+ // If we want all or we have a role on task, we show it
+ if (empty($mine) || ! empty($tasksrole[$lines[$i]->id]))
+ {
+ //dol_syslog("projectLinesPerWeek Found line ".$i.", a qualified task (i have role or want to show all tasks) with id=".$lines[$i]->id." project id=".$lines[$i]->fk_project);
+
+ // Break on a new project
+ if ($parent == 0 && $lines[$i]->fk_project != $lastprojectid)
+ {
+ $lastprojectid=$lines[$i]->fk_project;
+ $projectstatic->id = $lines[$i]->fk_project;
+ }
+
+ //var_dump('--- '.$level.' '.$firstdaytoshow.' '.$fuser->id.' '.$projectstatic->id.' '.$workloadforid[$projectstatic->id]);
+ //var_dump($projectstatic->weekWorkLoadPerTask);
+ if (empty($workloadforid[$projectstatic->id]))
+ {
+ $projectstatic->loadTimeSpentMonth($firstdaytoshow, 0, $fuser->id); // Load time spent from table projet_task_time for the project into this->weekWorkLoad and this->weekWorkLoadPerTask for all days of a week
+ $workloadforid[$projectstatic->id]=1;
+ }
+ //var_dump($projectstatic->weekWorkLoadPerTask);
+ //var_dump('--- '.$projectstatic->id.' '.$workloadforid[$projectstatic->id]);
+
+ $projectstatic->id=$lines[$i]->fk_project;
+ $projectstatic->ref=$lines[$i]->projectref;
+ $projectstatic->title=$lines[$i]->projectlabel;
+ $projectstatic->public=$lines[$i]->public;
+ $projectstatic->thirdparty_name=$lines[$i]->thirdparty_name;
+
+ $taskstatic->id=$lines[$i]->id;
+ $taskstatic->ref=($lines[$i]->ref?$lines[$i]->ref:$lines[$i]->id);
+ $taskstatic->label=$lines[$i]->label;
+ $taskstatic->date_start=$lines[$i]->date_start;
+ $taskstatic->date_end=$lines[$i]->date_end;
+
+ $thirdpartystatic->id=$lines[$i]->thirdparty_id;
+ $thirdpartystatic->name=$lines[$i]->thirdparty_name;
+ $thirdpartystatic->email=$lines[$i]->thirdparty_email;
+
+ if (empty($oldprojectforbreak) || ($oldprojectforbreak != -1 && $oldprojectforbreak != $projectstatic->id))
+ {
+ print '
\n";
+ }
+
+ // Call to show task with a lower level (task under the current task)
+ $inc++;
+ $level++;
+ if ($lines[$i]->id > 0)
+ {
+ //var_dump('totalforeachday after taskid='.$lines[$i]->id.' and previous one on level '.$level);
+ //var_dump($totalforeachday);
+ $ret = projectLinesPerMonth($inc, $firstdaytoshow, $fuser, $lines[$i]->id, ($parent == 0 ? $lineswithoutlevel0 : $lines), $level, $projectsrole, $tasksrole, $mine, $restricteditformytask, $isavailable, $oldprojectforbreak, $TWeek);
+ //var_dump('ret with parent='.$lines[$i]->id.' level='.$level);
+ //var_dump($ret);
+ foreach($ret as $key => $val)
+ {
+ $totalforeachweek[$key]+=$val;
+ }
+ //var_dump('totalforeachday after taskid='.$lines[$i]->id.' and previous one on level '.$level.' + subtasks');
+ //var_dump($totalforeachday);
+ }
+ $level--;
+ }
+ else
+ {
+ //$level--;
+ }
+ }
+
+ return $totalforeachweek;
+}
+
/**
* Search in task lines with a particular parent if there is a task for a particular user (in taskrole)
diff --git a/htdocs/langs/en_US/projects.lang b/htdocs/langs/en_US/projects.lang
index 0f9c1d8f1b3..14d0e9d2b19 100644
--- a/htdocs/langs/en_US/projects.lang
+++ b/htdocs/langs/en_US/projects.lang
@@ -69,6 +69,7 @@ NewTask=New task
AddTask=Create task
AddTimeSpent=Create time spent
AddHereTimeSpentForDay=Add here time spent for this day/task
+AddHereTimeSpentForWeek=Add here time spent for this week/task
Activity=Activity
Activities=Tasks/activities
MyActivities=My tasks/activities
@@ -187,6 +188,7 @@ ProjectMustBeValidatedFirst=Project must be validated first
FirstAddRessourceToAllocateTime=Assign a user resource to task to allocate time
InputPerDay=Input per day
InputPerWeek=Input per week
+InputPerMonth=Input per month
InputDetail=Input detail
TimeAlreadyRecorded=This is time spent already recorded for this task/day and user %s
ProjectsWithThisUserAsContact=Projects with this user as contact
diff --git a/htdocs/langs/fr_FR/projects.lang b/htdocs/langs/fr_FR/projects.lang
index 388de918e05..e1de5b386a1 100644
--- a/htdocs/langs/fr_FR/projects.lang
+++ b/htdocs/langs/fr_FR/projects.lang
@@ -69,6 +69,7 @@ NewTask=Nouvelle tâche
AddTask=Créer tâche
AddTimeSpent=Saisir temps consommé
AddHereTimeSpentForDay=Ajoutez ici le temps passé pour cette journée/tâche
+AddHereTimeSpentForWeek=Ajoutez ici le temps passé pour cette semaine/tâche
Activity=Activité
Activities=Tâches/activités
MyActivities=Mes tâches/activités
@@ -187,6 +188,7 @@ ProjectMustBeValidatedFirst=Le projet doit être validé d'abord
FirstAddRessourceToAllocateTime=Affecter un utilisateur pour saisir des temps
InputPerDay=Saisie par jour
InputPerWeek=Saisie par semaine
+InputPerMonth=Saisie par mois
InputDetail=Saisir le détail
TimeAlreadyRecorded=C'est le temps passé déjà enregistré pour cette tâche/jour et pour l'utilisateur %s
ProjectsWithThisUserAsContact=Projets avec cet utilisateur comme contact
diff --git a/htdocs/projet/activity/perday.php b/htdocs/projet/activity/perday.php
index 8215eec4258..9e056ba5029 100644
--- a/htdocs/projet/activity/perday.php
+++ b/htdocs/projet/activity/perday.php
@@ -146,7 +146,9 @@ $search_array_options_task = $extrafields->getOptionalsFromPost($object->table_e
/*
* Actions
*/
-
+$parameters = array('id' => $id, 'taskid' => $taskid, 'projectid' => $projectid);
+$reshook=$hookmanager->executeHooks('doActions',$parameters,$object,$action); // Note that $action and $object may have been modified by some hooks
+if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
// Purge criteria
if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter.x', 'alpha') || GETPOST('button_removefilter', 'alpha')) // All tests are required to be compatible with all browsers
{
diff --git a/htdocs/projet/activity/perweek.php b/htdocs/projet/activity/perweek.php
index 8b69fc6aa01..085b54b9bdb 100644
--- a/htdocs/projet/activity/perweek.php
+++ b/htdocs/projet/activity/perweek.php
@@ -162,7 +162,9 @@ $search_array_options_task = $extrafields->getOptionalsFromPost('projet_task', '
/*
* Actions
*/
-
+$parameters = array('id' => $id, 'taskid' => $taskid, 'projectid' => $projectid);
+$reshook=$hookmanager->executeHooks('doActions',$parameters,$object,$action); // Note that $action and $object may have been modified by some hooks
+if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
// Purge criteria
if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter.x', 'alpha') || GETPOST('button_removefilter', 'alpha')) // All tests are required to be compatible with all browsers
{
diff --git a/htdocs/projet/class/project.class.php b/htdocs/projet/class/project.class.php
index 54d13a51549..048ea819187 100644
--- a/htdocs/projet/class/project.class.php
+++ b/htdocs/projet/class/project.class.php
@@ -1797,6 +1797,72 @@ class Project extends CommonObject
return -1;
}
}
+ /**
+ * Load time spent into this->weekWorkLoad and this->weekWorkLoadPerTask for all day of a week of project.
+ * Note: array weekWorkLoad and weekWorkLoadPerTask are reset and filled at each call.
+ *
+ * @param int $datestart First day of week (use dol_get_first_day to find this date)
+ * @param int $taskid Filter on a task id
+ * @param int $userid Time spent by a particular user
+ * @return int <0 if OK, >0 if KO
+ */
+ public function loadTimeSpentMonth($datestart, $taskid=0, $userid=0)
+ {
+ $error=0;
+
+ $this->monthWorkLoad=array();
+ $this->monthWorkLoadPerTask=array();
+
+ if (empty($datestart)) dol_print_error('','Error datestart parameter is empty');
+
+ $sql = "SELECT ptt.rowid as taskid, ptt.task_duration, ptt.task_date, ptt.task_datehour, ptt.fk_task";
+ $sql.= " FROM ".MAIN_DB_PREFIX."projet_task_time AS ptt, ".MAIN_DB_PREFIX."projet_task as pt";
+ $sql.= " WHERE ptt.fk_task = pt.rowid";
+ $sql.= " AND pt.fk_projet = ".$this->id;
+ $sql.= " AND (ptt.task_date >= '".$this->db->idate($datestart)."' ";
+ $sql.= " AND ptt.task_date <= '".$this->db->idate(dol_time_plus_duree($datestart, 1, 'm') - 1)."')";
+ if ($task_id) $sql.= " AND ptt.fk_task=".$taskid;
+ if (is_numeric($userid)) $sql.= " AND ptt.fk_user=".$userid;
+
+ //print $sql;
+ $resql=$this->db->query($sql);
+ if ($resql)
+ {
+ $weekalreadyfound=array();
+
+ $num = $this->db->num_rows($resql);
+ $i = 0;
+ // Loop on each record found, so each couple (project id, task id)
+ while ($i < $num)
+ {
+ $obj=$this->db->fetch_object($resql);
+ if(!empty($obj->task_date)) {
+ $date = explode('-',$obj->task_date);
+ $week_number = getWeekNumber($date[2], $date[1], $date[0]);
+ }
+ if (empty($weekalreadyfound[$week_number]))
+ {
+ $this->monthWorkLoad[$week_number] = $obj->task_duration;
+ $this->monthWorkLoadPerTask[$week_number][$obj->fk_task] = $obj->task_duration;
+ }
+ else
+ {
+ $this->monthWorkLoad[$week_number] += $obj->task_duration;
+ $this->monthWorkLoadPerTask[$week_number][$obj->fk_task] += $obj->task_duration;
+ }
+ $weekalreadyfound[$week_number]=1;
+ $i++;
+ }
+ $this->db->free($resql);
+ return 1;
+ }
+ else
+ {
+ $this->error="Error ".$this->db->lasterror();
+ dol_syslog(get_class($this)."::fetch ".$this->error, LOG_ERR);
+ return -1;
+ }
+ }
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps