';
+
// Tick to drag and drop
if ($addordertick)
{
@@ -478,13 +483,13 @@ function projectLinesa(&$inc, $parent, &$lines, &$level, $var, $showproject, &$t
print '
';
diff --git a/htdocs/langs/en_US/admin.lang b/htdocs/langs/en_US/admin.lang
index ccf11407bc3..13368cbfc06 100755
--- a/htdocs/langs/en_US/admin.lang
+++ b/htdocs/langs/en_US/admin.lang
@@ -964,6 +964,7 @@ DelaysBeforeWarning=Delays before warning
DelaysOfToleranceBeforeWarning=Tolerance delays before warning
DelaysOfToleranceDesc=This screen allows you to define the tolerated delays before an alert is reported on screen with picto %s for each late element.
Delays_MAIN_DELAY_ACTIONS_TODO=Delay tolerance (in days) before alert on planned events not yet realised
+Delays_MAIN_DELAY_TASKS_TODO=Delay tolerance (in days) before alert on planned tasks not yet realised
Delays_MAIN_DELAY_ORDERS_TO_PROCESS=Delay tolerance (in days) before alert on orders not yet processed
Delays_MAIN_DELAY_SUPPLIER_ORDERS_TO_PROCESS=Delay tolerance (in days) before alert on suppliers orders not yet processed
Delays_MAIN_DELAY_PROPALS_TO_CLOSE=Delay tolerance (in days) before alert on proposals to close
diff --git a/htdocs/langs/en_US/projects.lang b/htdocs/langs/en_US/projects.lang
index 4bb6a52af8b..b60624df05a 100644
--- a/htdocs/langs/en_US/projects.lang
+++ b/htdocs/langs/en_US/projects.lang
@@ -29,6 +29,7 @@ OfficerProject=Officer project
LastProjects=Last %s projects
AllProjects=All projects
OpenedProjects=Opened projects
+OpenedTasks=Opened tasks
OpportunitiesStatusForOpenedProjects=Opportunities amount of opened projects by status
ProjectsList=List of projects
ShowProject=Show project
diff --git a/htdocs/projet/class/project.class.php b/htdocs/projet/class/project.class.php
index 219736a62a7..2c67d41549d 100644
--- a/htdocs/projet/class/project.class.php
+++ b/htdocs/projet/class/project.class.php
@@ -891,6 +891,10 @@ class Project extends CommonObject
$label .= ($label?' ':'').'' . $langs->trans('Ref') . ': ' . $this->ref; // The space must be after the : to not being explode when showing the title in img_picto
if (! empty($this->title))
$label .= ($label?' ':'').'' . $langs->trans('Label') . ': ' . $this->title; // The space must be after the : to not being explode when showing the title in img_picto
+ if (! empty($this->dateo))
+ $label .= ($label?' ':'').'' . $langs->trans('DateStart') . ': ' . dol_print_date($this->dateo, 'day'); // The space must be after the : to not being explode when showing the title in img_picto
+ if (! empty($this->datee))
+ $label .= ($label?' ':'').'' . $langs->trans('DateEnd') . ': ' . dol_print_date($this->datee, 'day'); // The space must be after the : to not being explode when showing the title in img_picto
if ($moreinpopup) $label.=' '.$moreinpopup;
$linkclose = '" title="'.dol_escape_htmltag($label, 1).'" class="classfortooltip">';
@@ -1558,6 +1562,70 @@ class Project extends CommonObject
}
}
+
+ /**
+ * Load indicators for dashboard (this->nbtodo and this->nbtodolate)
+ *
+ * @param User $user Objet user
+ * @return WorkboardResponse|int <0 if KO, WorkboardResponse if OK
+ */
+ function load_board($user)
+ {
+ global $conf, $langs;
+
+ $mine=0; $socid=$user->societe_id;
+
+ $projectsListId = $this->getProjectsAuthorizedForUser($user,$mine?$mine:($user->rights->projet->all->lire?2:0),1,$socid);
+
+ $sql = "SELECT p.rowid, p.fk_statut as status, p.fk_opp_status, p.datee as datee";
+ $sql.= " FROM (".MAIN_DB_PREFIX."projet as p";
+ $sql.= ")";
+ $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s on p.fk_soc = s.rowid";
+ if (! $user->rights->societe->client->voir && ! $socid) $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON sc.fk_soc = s.rowid";
+ $sql.= " WHERE p.fk_statut = 1";
+ $sql.= " AND p.entity IN (".getEntity('project').')';
+ if ($mine || ! $user->rights->projet->all->lire) $sql.= " AND p.rowid IN (".$projectsListId.")";
+ // No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser
+ //if ($socid || ! $user->rights->societe->client->voir) $sql.= " AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = ".$socid.")";
+ if ($socid) $sql.= " AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = ".$socid.")";
+ if (! $user->rights->societe->client->voir && ! $socid) $sql.= " AND ((s.rowid = sc.fk_soc AND sc.fk_user = " .$user->id.") OR (s.rowid IS NULL))";
+
+ $resql=$this->db->query($sql);
+ if ($resql)
+ {
+ $project_static = new Project($this->db);
+
+ $response = new WorkboardResponse();
+ $response->warning_delay = $conf->projet->warning_delay/60/60/24;
+ $response->label = $langs->trans("OpenedProjects");
+ if ($user->rights->projet->all->lire) $response->url = DOL_URL_ROOT.'/projet/index.php?search_status=1&mainmenu=project';
+ else $response->url = DOL_URL_ROOT.'/projet/index.php?mode=mine&search_status=1&mainmenu=project';
+ $response->img = img_object($langs->trans("Projects"),"project");
+
+ // This assignment in condition is not a bug. It allows walking the results.
+ while ($obj=$this->db->fetch_object($resql))
+ {
+ $response->nbtodo++;
+
+ $project_static->statut = $obj->status;
+ $project_static->opp_status = $obj->opp_status;
+ $project_static->datee = $this->db->jdate($obj->datee);
+
+ if ($project_static->hasDelay()) {
+ $response->nbtodolate++;
+ }
+ }
+
+ return $response;
+ }
+ else
+ {
+ $this->error=$this->db->error();
+ return -1;
+ }
+ }
+
+
/**
* Function used to replace a thirdparty id with another one.
*
@@ -1574,6 +1642,24 @@ class Project extends CommonObject
return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables);
}
+
+ /**
+ * Is the action delayed?
+ *
+ * @return bool
+ */
+ public function hasDelay()
+ {
+ global $conf;
+
+ if (! ($this->statut == 1)) {
+ return false;
+ }
+
+ $now = dol_now();
+
+ return $this->datee < ($now - $conf->projet->warning_delay);
+ }
}
diff --git a/htdocs/projet/class/task.class.php b/htdocs/projet/class/task.class.php
index ca8ed73e10d..ab85f4a9ce6 100644
--- a/htdocs/projet/class/task.class.php
+++ b/htdocs/projet/class/task.class.php
@@ -43,6 +43,7 @@ class Task extends CommonObject
var $date_start;
var $date_end;
var $progress;
+ var $fk_statut;
var $priority;
var $fk_user_creat;
var $fk_user_valid;
@@ -535,13 +536,14 @@ class Task extends CommonObject
$this->id=0;
$this->fk_projet='';
- $this->ref='';
+ $this->ref='TK01';
$this->fk_task_parent='';
- $this->title='';
+ $this->title='Specimen task TK01';
$this->duration_effective='';
$this->fk_user_creat='';
- $this->statut='';
- $this->note='';
+ $this->progress='25';
+ $this->fk_statut='';
+ $this->note='This is a specimen task not';
}
/**
@@ -569,8 +571,8 @@ class Task extends CommonObject
//print $usert.'-'.$userp.'-'.$projectid.'-'.$socid.'-'.$mode.' ';
// List of tasks (does not care about permissions. Filtering will be done later)
- $sql = "SELECT p.rowid as projectid, p.ref, p.title as plabel, p.public, p.fk_statut,";
- $sql.= " t.rowid as taskid, t.ref as taskref, t.label, t.description, t.fk_task_parent, t.duration_effective, t.progress,";
+ $sql = "SELECT p.rowid as projectid, p.ref, p.title as plabel, p.public, p.fk_statut as projectstatus,";
+ $sql.= " t.rowid as taskid, t.ref as taskref, t.label, t.description, t.fk_task_parent, t.duration_effective, t.progress, t.fk_statut as status,";
$sql.= " t.dateo as date_start, t.datee as date_end, t.planned_workload, t.rang";
if ($mode == 0)
{
@@ -674,13 +676,14 @@ class Task extends CommonObject
$tasks[$i]->fk_project = $obj->projectid;
$tasks[$i]->projectref = $obj->ref;
$tasks[$i]->projectlabel = $obj->plabel;
- $tasks[$i]->projectstatus = $obj->fk_statut;
+ $tasks[$i]->projectstatus = $obj->projectstatus;
$tasks[$i]->label = $obj->label;
$tasks[$i]->description = $obj->description;
$tasks[$i]->fk_parent = $obj->fk_task_parent;
$tasks[$i]->duration = $obj->duration_effective;
$tasks[$i]->planned_workload= $obj->planned_workload;
$tasks[$i]->progress = $obj->progress;
+ $tasks[$i]->fk_statut = $obj->status;
$tasks[$i]->public = $obj->public;
$tasks[$i]->date_start = $this->db->jdate($obj->date_start);
$tasks[$i]->date_end = $this->db->jdate($obj->date_end);
@@ -1542,4 +1545,91 @@ class Task extends CommonObject
return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref);
}
+
+ /**
+ * Load indicators for dashboard (this->nbtodo and this->nbtodolate)
+ *
+ * @param User $user Objet user
+ * @return WorkboardResponse|int <0 if KO, WorkboardResponse if OK
+ */
+ function load_board($user)
+ {
+ global $conf, $langs;
+
+ $mine=0; $socid=$user->societe_id;
+
+ $projectstatic = new Project($this->db);
+ $projectsListId = $projectstatic->getProjectsAuthorizedForUser($user,$mine,1,$socid);
+
+ // List of tasks (does not care about permissions. Filtering will be done later)
+ $sql = "SELECT p.rowid as projectid, p.fk_statut as projectstatus,";
+ $sql.= " t.rowid as taskid, t.progress as progress, t.fk_statut as status,";
+ $sql.= " t.dateo as date_start, t.datee as datee";
+ $sql.= " FROM ".MAIN_DB_PREFIX."projet as p";
+ $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s on p.fk_soc = s.rowid";
+ if (! $user->rights->societe->client->voir && ! $socid) $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON sc.fk_soc = s.rowid";
+ $sql.= ", ".MAIN_DB_PREFIX."projet_task as t";
+ $sql.= " WHERE p.entity IN (".getEntity('project').')';
+ $sql.= " AND p.fk_statut = 1";
+ $sql.= " AND t.fk_projet = p.rowid";
+ $sql.= " AND t.progress < 100"; // tasks to do
+ if ($mine || ! $user->rights->projet->all->lire) $sql.= " AND p.rowid IN (".$projectsListId.")";
+ // No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser
+ //if ($socid || ! $user->rights->societe->client->voir) $sql.= " AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = ".$socid.")";
+ if ($socid) $sql.= " AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = ".$socid.")";
+ if (! $user->rights->societe->client->voir && ! $socid) $sql.= " AND ((s.rowid = sc.fk_soc AND sc.fk_user = " .$user->id.") OR (s.rowid IS NULL))";
+ //print $sql;
+ $resql=$this->db->query($sql);
+ if ($resql)
+ {
+ $task_static = new Task($this->db);
+
+ $response = new WorkboardResponse();
+ $response->warning_delay = $conf->projet->task->warning_delay/60/60/24;
+ $response->label = $langs->trans("OpenedTasks");
+ if ($user->rights->projet->all->lire) $response->url = DOL_URL_ROOT.'/projet/tasks/list.php?mainmenu=project';
+ else $response->url = DOL_URL_ROOT.'/projet/tasks/list.php?mode=mine&mainmenu=project';
+ $response->img = img_object($langs->trans("Tasks"),"task");
+
+ // This assignment in condition is not a bug. It allows walking the results.
+ while ($obj=$this->db->fetch_object($resql))
+ {
+ $response->nbtodo++;
+
+ $task_static->projectstatus = $obj->projectstatus;
+ $task_static->progress = $obj->progress;
+ $task_static->fk_statut = $obj->status;
+ $task_static->datee = $this->db->jdate($obj->datee);
+
+ if ($task_static->hasDelay()) {
+ $response->nbtodolate++;
+ }
+ }
+
+ return $response;
+ }
+ else
+ {
+ $this->error=$this->db->error();
+ return -1;
+ }
+ }
+
+ /**
+ * Is the action delayed?
+ *
+ * @return bool
+ */
+ public function hasDelay()
+ {
+ global $conf;
+
+ if (! ($this->progress >= 0 && $this->progress < 100)) {
+ return false;
+ }
+
+ $now = dol_now();
+
+ return ($this->datee > 0 && $this->datee < ($now - $conf->projet->task->warning_delay));
+ }
}
diff --git a/htdocs/projet/index.php b/htdocs/projet/index.php
index e1bc581453d..23d6df9be2b 100644
--- a/htdocs/projet/index.php
+++ b/htdocs/projet/index.php
@@ -197,7 +197,7 @@ print "";
print ' ';
-print_projecttasks_array($db,$form,$socid,$projectsListId,0,1,$listofoppstatus);
+print_projecttasks_array($db, $form, $socid, $projectsListId, 0, 1, $listofoppstatus, array());
diff --git a/htdocs/projet/list.php b/htdocs/projet/list.php
index d0a1ac2c599..5c9f102f772 100644
--- a/htdocs/projet/list.php
+++ b/htdocs/projet/list.php
@@ -198,7 +198,7 @@ if ($search_user > 0)
$sql.=", ".MAIN_DB_PREFIX."c_type_contact as tc";
}
-$sql.= " WHERE p.entity = ".$conf->entity;
+$sql.= " WHERE p.entity IN (".getEntity('project').')';
if ($mine || ! $user->rights->projet->all->lire) $sql.= " AND p.rowid IN (".$projectsListId.")";
// No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser
//if ($socid || ! $user->rights->societe->client->voir) $sql.= " AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = ".$socid.")";
diff --git a/htdocs/projet/tasks.php b/htdocs/projet/tasks.php
index eb95fd33624..4abb9122eb2 100644
--- a/htdocs/projet/tasks.php
+++ b/htdocs/projet/tasks.php
@@ -431,9 +431,9 @@ else if ($id > 0 || ! empty($ref))
print '
'.$langs->trans("DateStart").'
';
print '
'.$langs->trans("DateEnd").'
';
print '
'.$langs->trans("PlannedWorkload").'
';
- print '
'.$langs->trans("ProgressDeclared").'
';
print '
'.$langs->trans("TimeSpent").'
';
print '
'.$langs->trans("ProgressCalculated").'
';
+ print '
'.$langs->trans("ProgressDeclared").'
';
print '
';
print "
\n";
if (count($tasksarray) > 0)
diff --git a/htdocs/projet/tasks/list.php b/htdocs/projet/tasks/list.php
index d23617192b3..fb2fcad6a38 100644
--- a/htdocs/projet/tasks/list.php
+++ b/htdocs/projet/tasks/list.php
@@ -36,8 +36,8 @@ $id=GETPOST('id','int');
$search_all=GETPOST('search_all');
$search_project=GETPOST('search_project');
-if (! isset($_GET['search_status']) && ! isset($_POST['search_status'])) $search_status=1;
-else $search_status=GETPOST('search_status');
+if (! isset($_GET['search_projectstatus']) && ! isset($_POST['search_projectstatus'])) $search_projectstatus=1;
+else $search_projectstatus=GETPOST('search_projectstatus');
$search_task_ref=GETPOST('search_task_ref');
$search_task_label=GETPOST('search_task_label');
$search_project_user=GETPOST('search_project_user');
@@ -62,13 +62,13 @@ if (GETPOST("button_removefilter_x") || GETPOST("button_removefilter.x") || GETP
{
$search_all="";
$search_project="";
- $search_status="";
+ $search_projectstatus="";
$search_task_ref="";
$search_task_label="";
$search_task_user=-1;
$search_project_user=-1;
}
-if (empty($search_status) && $search_status == '') $search_status=1;
+if (empty($search_projectstatus) && $search_projectstatus == '') $search_projectstatus=1;
// List of fields to search into when doing a "search in all"
$fieldstosearchall = array(
@@ -128,8 +128,8 @@ if ($search_all) $morewherefilter.=natural_search(array_keys($fieldstosea
if ($search_task_ref) $morewherefilter.=natural_search('t.ref', $search_task_ref);
if ($search_task_label) $morewherefilter.=natural_search('t.label', $search_task_label);
-$tasksarray=$taskstatic->getTasksArray(0, 0, $projectstatic->id, $socid, 0, $search_project, $search_status, $morewherefilter, $search_project_user, 0); // We don't use filter on task user. Because sometimes a task is assigned but not the parent one and we want to show also parent, so filtering is done during output
-$tasksrole=$taskstatic->getUserRolesForProjectsOrTasks(0, ($tuser->id?$tuser:null), $projectstatic->id, 0, $search_status); // We load also tasks limited to a particular user
+$tasksarray=$taskstatic->getTasksArray(0, 0, $projectstatic->id, $socid, 0, $search_project, $search_projectstatus, $morewherefilter, $search_project_user, 0); // We don't use filter on task user. Because sometimes a task is assigned but not the parent one and we want to show also parent, so filtering is done during output
+$tasksrole=$taskstatic->getUserRolesForProjectsOrTasks(0, ($tuser->id?$tuser:null), $projectstatic->id, 0, $search_projectstatus); // We load also tasks limited to a particular user
print '