1<?php 2/* Copyright (C) 2005 Rodolphe Quiedeville <rodolphe@quiedeville.org> 3 * Copyright (C) 2004-2016 Laurent Destailleur <eldy@users.sourceforge.net> 4 * Copyright (C) 2005-2010 Regis Houssin <regis.houssin@inodbox.com> 5 * Copyright (C) 2010 François Legastelois <flegastelois@teclib.com> 6 * Copyright (C) 2018 Frédéric France <frederic.france@netlogic.fr> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 3 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program. If not, see <https://www.gnu.org/licenses/>. 20 */ 21 22/** 23 * \file htdocs/projet/activity/perday.php 24 * \ingroup projet 25 * \brief List activities of tasks (per day entry) 26 */ 27 28require "../../main.inc.php"; 29require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php'; 30require_once DOL_DOCUMENT_ROOT.'/projet/class/task.class.php'; 31require_once DOL_DOCUMENT_ROOT.'/core/lib/project.lib.php'; 32require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php'; 33require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php'; 34require_once DOL_DOCUMENT_ROOT.'/core/class/html.formprojet.class.php'; 35require_once DOL_DOCUMENT_ROOT.'/core/class/html.formcompany.class.php'; 36require_once DOL_DOCUMENT_ROOT.'/holiday/class/holiday.class.php'; 37 38// Load translation files required by the page 39$langs->loadLangs(array('projects', 'users', 'companies')); 40 41$action = GETPOST('action', 'aZ09'); 42$mode = GETPOST("mode", 'alpha'); 43$id = GETPOST('id', 'int'); 44$taskid = GETPOST('taskid', 'int'); 45 46$contextpage = GETPOST('contextpage', 'aZ') ?GETPOST('contextpage', 'aZ') : 'perdaycard'; 47 48$mine = 0; 49if ($mode == 'mine') $mine = 1; 50 51$projectid = isset($_GET["id"]) ? GETPOST("id", "int", 1) : GETPOST("projectid", "int"); 52 53$hookmanager->initHooks(array('timesheetperdaycard')); 54 55// Security check 56$socid = 0; 57// For external user, no check is done on company because readability is managed by public status of project and assignement. 58//if ($user->socid > 0) $socid=$user->socid; 59$result = restrictedArea($user, 'projet', $projectid); 60 61$now = dol_now(); 62$nowtmp = dol_getdate($now); 63$nowday = $nowtmp['mday']; 64$nowmonth = $nowtmp['mon']; 65$nowyear = $nowtmp['year']; 66 67$year = GETPOST('reyear', 'int') ?GETPOST('reyear', 'int') : (GETPOST("year", "int") ?GETPOST("year", "int") : (GETPOST("addtimeyear", "int") ?GETPOST("addtimeyear", "int") : date("Y"))); 68$month = GETPOST('remonth', 'int') ?GETPOST('remonth', 'int') : (GETPOST("month", "int") ?GETPOST("month", "int") : (GETPOST("addtimemonth", "int") ?GETPOST("addtimemonth", "int") : date("m"))); 69$day = GETPOST('reday', 'int') ?GETPOST('reday', 'int') : (GETPOST("day", "int") ?GETPOST("day", "int") : (GETPOST("addtimeday", "int") ?GETPOST("addtimeday", "int") : date("d"))); 70$week = GETPOST("week", "int") ?GETPOST("week", "int") : date("W"); 71 72$day = (int) $day; 73 74$search_categ = GETPOST("search_categ", 'alpha'); 75$search_usertoprocessid = GETPOST('search_usertoprocessid', 'int'); 76$search_task_ref = GETPOST('search_task_ref', 'alpha'); 77$search_task_label = GETPOST('search_task_label', 'alpha'); 78$search_project_ref = GETPOST('search_project_ref', 'alpha'); 79$search_thirdparty = GETPOST('search_thirdparty', 'alpha'); 80$search_declared_progress = GETPOST('search_declared_progress', 'alpha'); 81 82$monthofday = GETPOST('addtimemonth'); 83$dayofday = GETPOST('addtimeday'); 84$yearofday = GETPOST('addtimeyear'); 85 86$daytoparse = $now; 87if ($yearofday && $monthofday && $dayofday) $daytoparse = dol_mktime(0, 0, 0, $monthofday, $dayofday, $yearofday); // xxxofday is value of day after submit action 'addtime' 88elseif ($year && $month && $day) $daytoparse = dol_mktime(0, 0, 0, $month, $day, $year); // this are value submited after submit of action 'submitdateselect' 89 90 91if (empty($search_usertoprocessid) || $search_usertoprocessid == $user->id) 92{ 93 $usertoprocess = $user; 94 $search_usertoprocessid = $usertoprocess->id; 95} elseif ($search_usertoprocessid > 0) 96{ 97 $usertoprocess = new User($db); 98 $usertoprocess->fetch($search_usertoprocessid); 99 $search_usertoprocessid = $usertoprocess->id; 100} else { 101 $usertoprocess = new User($db); 102} 103 104$object = new Task($db); 105$project = new Project($db); 106 107// Extra fields 108$extrafields = new ExtraFields($db); 109 110// fetch optionals attributes and labels 111$extrafields->fetch_name_optionals_label($object->table_element); 112 113// Definition of fields for list 114$arrayfields = array(); 115$arrayfields['t.planned_workload'] = array('label'=>'PlannedWorkload', 'checked'=>1, 'enabled'=>1, 'position'=>0); 116$arrayfields['t.progress'] = array('label'=>'ProgressDeclared', 'checked'=>1, 'enabled'=>1, 'position'=>0); 117/*$arrayfields=array( 118 // Project 119 'p.opp_amount'=>array('label'=>$langs->trans("OpportunityAmountShort"), 'checked'=>0, 'enabled'=>($conf->global->PROJECT_USE_OPPORTUNITIES?1:0), 'position'=>103), 120 'p.fk_opp_status'=>array('label'=>$langs->trans("OpportunityStatusShort"), 'checked'=>0, 'enabled'=>($conf->global->PROJECT_USE_OPPORTUNITIES?1:0), 'position'=>104), 121 'p.opp_percent'=>array('label'=>$langs->trans("OpportunityProbabilityShort"), 'checked'=>0, 'enabled'=>($conf->global->PROJECT_USE_OPPORTUNITIES?1:0), 'position'=>105), 122 'p.budget_amount'=>array('label'=>$langs->trans("Budget"), 'checked'=>0, 'position'=>110), 123 'p.usage_bill_time'=>array('label'=>$langs->trans("BillTimeShort"), 'checked'=>0, 'position'=>115), 124 ); 125 */ 126// Extra fields 127if (is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label']) > 0) 128{ 129 foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $val) 130 { 131 if (!empty($extrafields->attributes[$object->table_element]['list'][$key])) 132 $arrayfields["efpt.".$key] = array('label'=>$extrafields->attributes[$object->table_element]['label'][$key], 'checked'=>(($extrafields->attributes[$object->table_element]['list'][$key] < 0) ? 0 : 1), 'position'=>$extrafields->attributes[$object->table_element]['pos'][$key], 'enabled'=>(abs((int) $extrafields->attributes[$object->table_element]['list'][$key]) != 3 && $extrafields->attributes[$object->table_element]['perms'][$key])); 133 } 134} 135$arrayfields = dol_sort_array($arrayfields, 'position'); 136 137 138$search_array_options_project = $extrafields->getOptionalsFromPost($project->table_element, '', 'search_'); 139$search_array_options_task = $extrafields->getOptionalsFromPost($object->table_element, '', 'search_task_'); 140 141 142/* 143 * Actions 144 */ 145 146$parameters = array('id' => $id, 'taskid' => $taskid, 'projectid' => $projectid); 147$reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks 148if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'errors'); 149// Purge criteria 150if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter.x', 'alpha') || GETPOST('button_removefilter', 'alpha')) // All tests are required to be compatible with all browsers 151{ 152 $action = ''; 153 $search_categ = ''; 154 $search_usertoprocessid = $user->id; 155 $search_task_ref = ''; 156 $search_task_label = ''; 157 $search_project_ref = ''; 158 $search_thirdparty = ''; 159 $search_declared_progress = ''; 160 161 $search_array_options_project = array(); 162 $search_array_options_task = array(); 163 164 // We redefine $usertoprocess 165 $usertoprocess = $user; 166} 167if (GETPOST("button_search_x", 'alpha') || GETPOST("button_search.x", 'alpha') || GETPOST("button_search", 'alpha')) 168{ 169 $action = ''; 170} 171 172if (GETPOST('submitdateselect')) 173{ 174 if (GETPOST('remonth', 'int') && GETPOST('reday', 'int') && GETPOST('reyear', 'int')) 175 { 176 $daytoparse = dol_mktime(0, 0, 0, GETPOST('remonth', 'int'), GETPOST('reday', 'int'), GETPOST('reyear', 'int')); 177 } 178 179 $action = ''; 180} 181 182include DOL_DOCUMENT_ROOT.'/core/actions_changeselectedfields.inc.php'; 183 184if ($action == 'addtime' && $user->rights->projet->lire && GETPOST('assigntask') && GETPOST('formfilteraction') != 'listafterchangingselectedfields') 185{ 186 $action = 'assigntask'; 187 188 if ($taskid > 0) 189 { 190 $result = $object->fetch($taskid, $ref); 191 if ($result < 0) $error++; 192 } else { 193 setEventMessages($langs->transnoentitiesnoconv("ErrorFieldRequired", $langs->transnoentitiesnoconv("Task")), '', 'errors'); 194 $error++; 195 } 196 if (!GETPOST('type')) 197 { 198 setEventMessages($langs->transnoentitiesnoconv("ErrorFieldRequired", $langs->transnoentitiesnoconv("Type")), '', 'errors'); 199 $error++; 200 } 201 if (!$error) 202 { 203 $idfortaskuser = $usertoprocess->id; 204 $result = $object->add_contact($idfortaskuser, GETPOST("type"), 'internal'); 205 206 if ($result >= 0 || $result == -2) // Contact add ok or already contact of task 207 { 208 // Test if we are already contact of the project (should be rare but sometimes we can add as task contact without being contact of project, like when admin user has been removed from contact of project) 209 $sql = 'SELECT ec.rowid FROM '.MAIN_DB_PREFIX.'element_contact as ec, '.MAIN_DB_PREFIX.'c_type_contact as tc WHERE tc.rowid = ec.fk_c_type_contact'; 210 $sql .= ' AND ec.fk_socpeople = '.((int) $idfortaskuser)." AND ec.element_id = ".((int) $object->fk_project)." AND tc.element = 'project' AND source = 'internal'"; 211 $resql = $db->query($sql); 212 if ($resql) 213 { 214 $obj = $db->fetch_object($resql); 215 if (!$obj) // User is not already linked to project, so we will create link to first type 216 { 217 $project = new Project($db); 218 $project->fetch($object->fk_project); 219 // Get type 220 $listofprojcontact = $project->liste_type_contact('internal'); 221 222 if (count($listofprojcontact)) 223 { 224 $typeforprojectcontact = reset(array_keys($listofprojcontact)); 225 $result = $project->add_contact($idfortaskuser, $typeforprojectcontact, 'internal'); 226 } 227 } 228 } else { 229 dol_print_error($db); 230 } 231 } 232 } 233 234 if ($result < 0) 235 { 236 $error++; 237 if ($object->error == 'DB_ERROR_RECORD_ALREADY_EXISTS') 238 { 239 $langs->load("errors"); 240 setEventMessages($langs->trans("ErrorTaskAlreadyAssigned"), null, 'warnings'); 241 } else { 242 setEventMessages($object->error, $object->errors, 'errors'); 243 } 244 } 245 246 if (!$error) 247 { 248 setEventMessages("TaskAssignedToEnterTime", null); 249 $taskid = 0; 250 } 251 252 $action = ''; 253} 254 255if ($action == 'addtime' && $user->rights->projet->lire && GETPOST('formfilteraction') != 'listafterchangingselectedfields') 256{ 257 $timespent_duration = array(); 258 259 if (is_array($_POST)) 260 { 261 foreach ($_POST as $key => $time) 262 { 263 if (intval($time) > 0) 264 { 265 $matches = array(); 266 // Hours or minutes of duration 267 if (preg_match("/([0-9]+)duration(hour|min)/", $key, $matches)) 268 { 269 $id = $matches[1]; 270 if ($id > 0) 271 { 272 // We store HOURS in seconds 273 if ($matches[2] == 'hour') $timespent_duration[$id] += $time * 60 * 60; 274 275 // We store MINUTES in seconds 276 if ($matches[2] == 'min') $timespent_duration[$id] += $time * 60; 277 } 278 } 279 } 280 } 281 } 282 283 if (count($timespent_duration) > 0) 284 { 285 foreach ($timespent_duration as $key => $val) 286 { 287 $object->fetch($key); 288 $taskid = $object->id; 289 290 if (GETPOSTISSET($taskid.'progress')) $object->progress = GETPOST($taskid.'progress', 'int'); 291 else unset($object->progress); 292 293 $object->timespent_duration = $val; 294 $object->timespent_fk_user = $usertoprocess->id; 295 $object->timespent_note = GETPOST($key.'note'); 296 if (GETPOST($key."hour", 'int') != '' && GETPOST($key."hour", 'int') >= 0) // If hour was entered 297 { 298 $object->timespent_datehour = dol_mktime(GETPOST($key."hour", 'int'), GETPOST($key."min", 'int'), 0, $monthofday, $dayofday, $yearofday); 299 $object->timespent_withhour = 1; 300 } else { 301 $object->timespent_datehour = dol_mktime(12, 0, 0, $monthofday, $dayofday, $yearofday); 302 } 303 $object->timespent_date = $object->timespent_datehour; 304 305 if ($object->timespent_date > 0) 306 { 307 $result = $object->addTimeSpent($user); 308 } else { 309 setEventMessages("ErrorBadDate", null, 'errors'); 310 $error++; 311 break; 312 } 313 314 if ($result < 0) 315 { 316 setEventMessages($object->error, $object->errors, 'errors'); 317 $error++; 318 break; 319 } 320 } 321 322 if (!$error) 323 { 324 setEventMessages($langs->trans("RecordSaved"), null, 'mesgs'); 325 326 // Redirect to avoid submit twice on back 327 header('Location: '.$_SERVER["PHP_SELF"].'?'.($projectid ? 'id='.$projectid : '').($search_usertoprocessid ? '&search_usertoprocessid='.$search_usertoprocessid : '').($mode ? '&mode='.$mode : '').'&year='.$yearofday.'&month='.$monthofday.'&day='.$dayofday); 328 exit; 329 } 330 } else { 331 setEventMessages($langs->trans("ErrorTimeSpentIsEmpty"), null, 'errors'); 332 } 333} 334 335 336 337/* 338 * View 339 */ 340 341$form = new Form($db); 342$formother = new FormOther($db); 343$formcompany = new FormCompany($db); 344$formproject = new FormProjets($db); 345$projectstatic = new Project($db); 346$project = new Project($db); 347$taskstatic = new Task($db); 348$thirdpartystatic = new Societe($db); 349$holiday = new Holiday($db); 350 351$prev = dol_getdate($daytoparse - (24 * 3600)); 352$prev_year = $prev['year']; 353$prev_month = $prev['mon']; 354$prev_day = $prev['mday']; 355 356$next = dol_getdate($daytoparse + (24 * 3600)); 357$next_year = $next['year']; 358$next_month = $next['mon']; 359$next_day = $next['mday']; 360 361$title = $langs->trans("TimeSpent"); 362 363$projectsListId = $projectstatic->getProjectsAuthorizedForUser($usertoprocess, (empty($usertoprocess->id) ? 2 : 0), 1); // Return all project i have permission on. I want my tasks and some of my task may be on a public projet that is not my project 364 365if ($id) 366{ 367 $project->fetch($id); 368 $project->fetch_thirdparty(); 369} 370 371$onlyopenedproject = 1; // or -1 372$morewherefilter = ''; 373 374if ($search_project_ref) $morewherefilter .= natural_search(array("p.ref", "p.title"), $search_project_ref); 375if ($search_task_ref) $morewherefilter .= natural_search("t.ref", $search_task_ref); 376if ($search_task_label) $morewherefilter .= natural_search(array("t.ref", "t.label"), $search_task_label); 377if ($search_thirdparty) $morewherefilter .= natural_search("s.nom", $search_thirdparty); 378if ($search_declared_progress) $morewherefilter .= natural_search("t.progress", $search_declared_progress, 1); 379 380$sql = &$morewherefilter; 381 382/*$search_array_options = $search_array_options_project; 383$extrafieldsobjectprefix='efp.'; 384$search_options_pattern='search_options_'; 385$extrafieldsobjectkey='projet'; 386include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_sql.tpl.php'; 387*/ 388$search_array_options = $search_array_options_task; 389$extrafieldsobjectprefix = 'efpt.'; 390$search_options_pattern = 'search_task_options_'; 391$extrafieldsobjectkey = 'projet_task'; 392include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_sql.tpl.php'; 393 394$tasksarray = $taskstatic->getTasksArray(0, 0, ($project->id ? $project->id : 0), $socid, 0, $search_project_ref, $onlyopenedproject, $morewherefilter, ($search_usertoprocessid ? $search_usertoprocessid : 0), 0, $extrafields); // We want to see all task of opened project i am allowed to see and that match filter, not only my tasks. Later only mine will be editable later. 395if ($morewherefilter) // Get all task without any filter, so we can show total of time spent for not visible tasks 396{ 397 $tasksarraywithoutfilter = $taskstatic->getTasksArray(0, 0, ($project->id ? $project->id : 0), $socid, 0, '', $onlyopenedproject, '', ($search_usertoprocessid ? $search_usertoprocessid : 0)); // We want to see all task of opened project i am allowed to see and that match filter, not only my tasks. Later only mine will be editable later. 398} 399$projectsrole = $taskstatic->getUserRolesForProjectsOrTasks($usertoprocess, 0, ($project->id ? $project->id : 0), 0, $onlyopenedproject); 400$tasksrole = $taskstatic->getUserRolesForProjectsOrTasks(0, $usertoprocess, ($project->id ? $project->id : 0), 0, $onlyopenedproject); 401//var_dump($usertoprocess); 402//var_dump($projectsrole); 403//var_dump($taskrole); 404 405llxHeader("", $title, "", '', '', '', array('/core/js/timesheet.js')); 406 407//print_barre_liste($title, $page, $_SERVER["PHP_SELF"], "", $sortfield, $sortorder, "", $num, '', 'project'); 408 409$param = ''; 410$param .= ($mode ? '&mode='.urlencode($mode) : ''); 411$param .= ($search_project_ref ? '&search_project_ref='.urlencode($search_project_ref) : ''); 412$param .= ($search_usertoprocessid ? '&search_usertoprocessid='.urlencode($search_usertoprocessid) : ''); 413$param .= ($search_thirdparty ? '&search_thirdparty='.urlencode($search_thirdparty) : ''); 414$param .= ($search_task_ref ? '&search_task_ref='.urlencode($search_task_ref) : ''); 415$param .= ($search_task_label ? '&search_task_label='.urlencode($search_task_label) : ''); 416 417/*$search_array_options=$search_array_options_project; 418$search_options_pattern='search_options_'; 419include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_param.tpl.php'; 420*/ 421 422$search_array_options = $search_array_options_task; 423$search_options_pattern = 'search_task_options_'; 424include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_param.tpl.php'; 425 426// Show navigation bar 427$nav = '<a class="inline-block valignmiddle" href="?year='.$prev_year."&month=".$prev_month."&day=".$prev_day.$param.'">'.img_previous($langs->trans("Previous"))."</a>\n"; 428$nav .= dol_print_date(dol_mktime(0, 0, 0, $month, $day, $year), "%A").' '; 429$nav .= " <span id=\"month_name\">".dol_print_date(dol_mktime(0, 0, 0, $month, $day, $year), "day")." </span>\n"; 430$nav .= '<a class="inline-block valignmiddle" href="?year='.$next_year."&month=".$next_month."&day=".$next_day.$param.'">'.img_next($langs->trans("Next"))."</a>\n"; 431//$nav .= " (<a href=\"?year=".$nowyear."&month=".$nowmonth."&day=".$nowday.$param."\">".$langs->trans("Today")."</a>)"; 432$nav .= ' '.$form->selectDate(-1, '', 0, 0, 2, "addtime", 1, 1).' '; 433//$nav .= ' <input type="submit" name="submitdateselect" class="button valignmiddle" value="'.$langs->trans("Refresh").'">'; 434$nav .= ' <button type="submit" name="button_search_x" value="x" class="bordertransp"><span class="fa fa-search"></span></button>'; 435 436$picto = 'clock'; 437 438print '<form name="addtime" method="POST" action="'.$_SERVER["PHP_SELF"].($project->id > 0 ? '?id='.$project->id : '').'">'; 439print '<input type="hidden" name="token" value="'.newToken().'">'; 440print '<input type="hidden" name="action" value="addtime">'; 441print '<input type="hidden" name="formfilteraction" id="formfilteraction" value="list">'; 442print '<input type="hidden" name="contextpage" value="'.$contextpage.'">'; 443print '<input type="hidden" name="mode" value="'.$mode.'">'; 444$tmp = dol_getdate($daytoparse); 445print '<input type="hidden" name="addtimeyear" value="'.$tmp['year'].'">'; 446print '<input type="hidden" name="addtimemonth" value="'.$tmp['mon'].'">'; 447print '<input type="hidden" name="addtimeday" value="'.$tmp['mday'].'">'; 448 449$head = project_timesheet_prepare_head($mode, $usertoprocess); 450print dol_get_fiche_head($head, 'inputperday', $langs->trans('TimeSpent'), -1, $picto); 451 452// Show description of content 453print '<div class="hideonsmartphone opacitymedium">'; 454if ($mine || ($usertoprocess->id == $user->id)) print $langs->trans("MyTasksDesc").'.'.($onlyopenedproject ? ' '.$langs->trans("OnlyOpenedProject") : '').'<br>'; 455else { 456 if (empty($usertoprocess->id) || $usertoprocess->id < 0) 457 { 458 if ($user->rights->projet->all->lire && !$socid) print $langs->trans("ProjectsDesc").'.'.($onlyopenedproject ? ' '.$langs->trans("OnlyOpenedProject") : '').'<br>'; 459 else print $langs->trans("ProjectsPublicTaskDesc").'.'.($onlyopenedproject ? ' '.$langs->trans("OnlyOpenedProject") : '').'<br>'; 460 } 461} 462if ($mine || ($usertoprocess->id == $user->id)) 463{ 464 print $langs->trans("OnlyYourTaskAreVisible").'<br>'; 465} else { 466 print $langs->trans("AllTaskVisibleButEditIfYouAreAssigned").'<br>'; 467} 468print '</div>'; 469 470print dol_get_fiche_end(); 471 472 473print '<div class="floatright right'.($conf->dol_optimize_smallscreen ? ' centpercent' : '').'">'.$nav.'</div>'; // We move this before the assign to components so, the default submit button is not the assign to. 474 475print '<div class="colorbacktimesheet float valignmiddle">'; 476$titleassigntask = $langs->transnoentities("AssignTaskToMe"); 477if ($usertoprocess->id != $user->id) $titleassigntask = $langs->transnoentities("AssignTaskToUser", $usertoprocess->getFullName($langs)); 478print '<div class="taskiddiv inline-block">'; 479print img_picto('', 'projecttask'); 480$formproject->selectTasks($socid ? $socid : -1, $taskid, 'taskid', 32, 0, '-- '.$langs->trans("ChooseANotYetAssignedTask").' --', 1, 0, 0, '', '', 'all', $usertoprocess); 481print '</div>'; 482print ' '; 483print $formcompany->selectTypeContact($object, '', 'type', 'internal', 'rowid', 0, 'maxwidth150onsmartphone'); 484print '<input type="submit" class="button valignmiddle" name="assigntask" value="'.dol_escape_htmltag($titleassigntask).'">'; 485print '</div>'; 486 487print '<div class="clearboth" style="padding-bottom: 20px;"></div>'; 488 489 490$moreforfilter = ''; 491 492// Filter on categories 493/*if (! empty($conf->categorie->enabled)) 494{ 495 require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php'; 496 $moreforfilter.='<div class="divsearchfield">'; 497 $moreforfilter.=$langs->trans('ProjectCategories'). ': '; 498 $moreforfilter.=$formother->select_categories('project', $search_categ, 'search_categ', 1, 1, 'maxwidth300'); 499 $moreforfilter.='</div>'; 500}*/ 501 502// If the user can view user other than himself 503$moreforfilter .= '<div class="divsearchfield">'; 504$moreforfilter .= '<div class="inline-block hideonsmartphone"></div>'; 505$includeonly = 'hierarchyme'; 506if (empty($user->rights->user->user->lire)) $includeonly = array($user->id); 507$moreforfilter .= img_picto($langs->trans('User'), 'user').$form->select_dolusers($search_usertoprocessid ? $search_usertoprocessid : $usertoprocess->id, 'search_usertoprocessid', $user->rights->user->user->lire ? 0 : 0, null, 0, $includeonly, null, 0, 0, 0, '', 0, '', 'maxwidth200 marginleftonly'); 508$moreforfilter .= '</div>'; 509 510if (empty($conf->global->PROJECT_TIMESHEET_DISABLEBREAK_ON_PROJECT)) 511{ 512 $moreforfilter .= '<div class="divsearchfield">'; 513 $moreforfilter .= '<div class="inline-block"></div>'; 514 $moreforfilter .= img_picto($langs->trans('Project'), 'project').'<input type="text" size="4" name="search_project_ref" class="marginleftonly" value="'.dol_escape_htmltag($search_project_ref).'">'; 515 $moreforfilter .= '</div>'; 516 517 $moreforfilter .= '<div class="divsearchfield">'; 518 $moreforfilter .= '<div class="inline-block"></div>'; 519 $moreforfilter .= img_picto($langs->trans('ThirdParty'), 'company').'<input type="text" size="4" name="search_thirdparty" class="marginleftonly" value="'.dol_escape_htmltag($search_thirdparty).'">'; 520 $moreforfilter .= '</div>'; 521} 522 523if (!empty($moreforfilter)) 524{ 525 print '<div class="liste_titre liste_titre_bydiv centpercent">'; 526 print $moreforfilter; 527 $parameters = array(); 528 $reshook = $hookmanager->executeHooks('printFieldPreListTitle', $parameters); // Note that $action and $object may have been modified by hook 529 print $hookmanager->resPrint; 530 print '</div>'; 531} 532 533$varpage = empty($contextpage) ? $_SERVER["PHP_SELF"] : $contextpage; 534$selectedfields = $form->multiSelectArrayWithCheckbox('selectedfields', $arrayfields, $varpage); // This also change content of $arrayfields 535 536// This must be after the $selectedfields 537$addcolspan = 0; 538if (!empty($arrayfields['t.planned_workload']['checked'])) $addcolspan++; 539if (!empty($arrayfields['t.progress']['checked'])) $addcolspan++; 540foreach ($arrayfields as $key => $val) 541{ 542 if ($val['checked'] && substr($key, 0, 5) == 'efpt.') $addcolspan++; 543} 544 545print '<div class="div-table-responsive">'; 546print '<table class="tagtable liste'.($moreforfilter ? " listwithfilterbefore" : "").'" id="tablelines3">'."\n"; 547 548print '<tr class="liste_titre_filter">'; 549if (!empty($conf->global->PROJECT_TIMESHEET_DISABLEBREAK_ON_PROJECT)) print '<td class="liste_titre"><input type="text" size="4" name="search_project_ref" value="'.dol_escape_htmltag($search_project_ref).'"></td>'; 550if (!empty($conf->global->PROJECT_TIMESHEET_DISABLEBREAK_ON_PROJECT)) print '<td class="liste_titre"><input type="text" size="4" name="search_thirdparty" value="'.dol_escape_htmltag($search_thirdparty).'"></td>'; 551print '<td class="liste_titre"><input type="text" size="4" name="search_task_label" value="'.dol_escape_htmltag($search_task_label).'"></td>'; 552// TASK fields 553$search_options_pattern = 'search_task_options_'; 554$extrafieldsobjectkey = 'projet_task'; 555$extrafieldsobjectprefix = 'efpt.'; 556include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_input.tpl.php'; 557print '<td class="liste_titre"></td>'; 558if (!empty($arrayfields['t.planned_workload']['checked'])) 559{ 560 print '<td class="liste_titre right"><input type="text" size="4" name="search_declared_progress" value="'.dol_escape_htmltag($search_declared_progress).'"></td>'; 561} 562if (!empty($arrayfields['t.progress']['checked'])) 563{ 564 print '<td class="liste_titre"></td>'; 565} 566print '<td class="liste_titre"></td>'; 567print '<td class="liste_titre"></td>'; 568print '<td class="liste_titre"></td>'; 569print '<td class="liste_titre"></td>'; 570// Action column 571print '<td class="liste_titre nowrap right">'; 572$searchpicto = $form->showFilterAndCheckAddButtons(0); 573print $searchpicto; 574print '</td>'; 575print "</tr>\n"; 576 577print '<tr class="liste_titre">'; 578if (!empty($conf->global->PROJECT_TIMESHEET_DISABLEBREAK_ON_PROJECT)) print '<th>'.$langs->trans("Project").'</th>'; 579if (!empty($conf->global->PROJECT_TIMESHEET_DISABLEBREAK_ON_PROJECT)) print '<th>'.$langs->trans("ThirdParty").'</th>'; 580print '<th>'.$langs->trans("Task").'</th>'; 581// TASK fields 582$extrafieldsobjectkey = 'projet_task'; 583$extrafieldsobjectprefix = 'efpt.'; 584include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_title.tpl.php'; 585if (!empty($arrayfields['t.planned_workload']['checked'])) 586{ 587 print '<th class="right leftborder plannedworkload maxwidth100">'.$langs->trans("PlannedWorkload").'</th>'; 588} 589if (!empty($arrayfields['t.progress']['checked'])) 590{ 591 print '<th class="right maxwidth100">'.$langs->trans("ProgressDeclared").'</th>'; 592} 593/*print '<td class="right maxwidth100">'.$langs->trans("TimeSpent").'</td>'; 594if ($usertoprocess->id == $user->id) print '<td class="right maxwidth100">'.$langs->trans("TimeSpentByYou").'</td>'; 595else print '<td class="right maxwidth100">'.$langs->trans("TimeSpentByUser").'</td>';*/ 596print '<th class="right maxwidth100">'.$langs->trans("TimeSpent").'<br><span class="opacitymedium">'.$langs->trans("Everybody").'</span></th>'; 597print '<th class="right maxwidth100">'.$langs->trans("TimeSpent").($usertoprocess->firstname ? '<br><span class="opacitymedium">'.dol_trunc($usertoprocess->firstname, 10).'</span>' : '').'</th>'; 598print '<th class="center leftborder">'.$langs->trans("HourStart").'</td>'; 599 600// By default, we can edit only tasks we are assigned to 601$restrictviewformytask = ((!isset($conf->global->PROJECT_TIME_SHOW_TASK_NOT_ASSIGNED)) ? 2 : $conf->global->PROJECT_TIME_SHOW_TASK_NOT_ASSIGNED); 602 603// Get if user is available or not for each day 604$isavailable = array(); 605if (!empty($conf->global->MAIN_DEFAULT_WORKING_DAYS)) 606{ 607 $tmparray = explode('-', $conf->global->MAIN_DEFAULT_WORKING_DAYS); 608 if (count($tmparray) >= 2) 609 { 610 $numstartworkingday = $tmparray[0]; 611 $numendworkingday = $tmparray[1]; 612 } 613} 614 615$statusofholidaytocheck = Holiday::STATUS_APPROVED; 616$isavailablefordayanduser = $holiday->verifDateHolidayForTimestamp($usertoprocess->id, $daytoparse, $statusofholidaytocheck); // $daytoparse is a date with hours = 0 617$isavailable[$daytoparse] = $isavailablefordayanduser; // in projectLinesPerWeek later, we are using $firstdaytoshow and dol_time_plus_duree to loop on each day 618 619$test = num_public_holiday($daytoparse, $daytoparse + 86400, $mysoc->country_code); 620if ($test) $isavailable[$daytoparse] = array('morning'=>false, 'afternoon'=>false, 'morning_reason'=>'public_holiday', 'afternoon_reason'=>'public_holiday'); 621 622$tmparray = dol_getdate($daytoparse, true); // detail of current day 623// For monday, must be 0 for monday if MAIN_START_WEEK = 1, must be 1 for monday if MAIN_START_WEEK = 0 624$idw = ($tmparray['wday'] - (empty($conf->global->MAIN_START_WEEK) ? 0 : 1)); 625// numstartworkingday and numendworkingday are default start and end date of working days (1 means sunday if MAIN_START_WEEK is 0, 1 means monday if MAIN_START_WEEK is 1) 626$cssweekend = ''; 627if ((($idw + 1) < $numstartworkingday) || (($idw + 1) > $numendworkingday)) // This is a day is not inside the setup of working days, so we use a week-end css. 628{ 629 $cssweekend = 'weekend'; 630} 631 632$tmpday = dol_time_plus_duree($daytoparse, $idw, 'd'); 633 634$cssonholiday = ''; 635if (!$isavailable[$daytoparse]['morning'] && !$isavailable[$daytoparse]['afternoon']) $cssonholiday .= 'onholidayallday '; 636elseif (!$isavailable[$daytoparse]['morning']) $cssonholiday .= 'onholidaymorning '; 637elseif (!$isavailable[$daytoparse]['afternoon']) $cssonholiday .= 'onholidayafternoon '; 638 639print '<th class="center'.($cssonholiday ? ' '.$cssonholiday : '').($cssweekend ? ' '.$cssweekend : '').'">'.$langs->trans("Duration").'</th>'; 640print '<th class="center">'.$langs->trans("Note").'</th>'; 641//print '<td class="center"></td>'; 642print_liste_field_titre($selectedfields, $_SERVER["PHP_SELF"], "", '', '', '', $sortfield, $sortorder, 'center maxwidthsearch '); 643 644print "</tr>\n"; 645 646$colspan = 4 + (empty($conf->global->PROJECT_TIMESHEET_DISABLEBREAK_ON_PROJECT) ? 0 : 2); 647 648if ($conf->use_javascript_ajax) 649{ 650 print '<tr class="liste_total">'; 651 print '<td class="liste_total" colspan="'.($colspan - 1 + $addcolspan).'">'; 652 print $langs->trans("Total"); 653 print '</td>'; 654 print '<td class="liste_total leftborder">'; 655 //print ' - '.$langs->trans("ExpectedWorkedHours").': <strong>'.price($usertoprocess->weeklyhours, 1, $langs, 0, 0).'</strong>'; 656 print '</td>'; 657 658 print '<td class="liste_total center'.($cssonholiday ? ' '.$cssonholiday : '').($cssweekend ? ' '.$cssweekend : '').'"><div class="totalDay0"> </div></td>'; 659 660 print '<td class="liste_total"></td>'; 661 print '<td class="liste_total"></td>'; 662 print '</tr>'; 663} 664 665 666if (count($tasksarray) > 0) 667{ 668 //var_dump($tasksarray); // contains only selected tasks 669 //var_dump($tasksarraywithoutfilter); // contains all tasks (if there is a filter, not defined if no filter) 670 //var_dump($tasksrole); 671 672 $j = 0; 673 $level = 0; 674 $totalforvisibletasks = projectLinesPerDay($j, 0, $usertoprocess, $tasksarray, $level, $projectsrole, $tasksrole, $mine, $restrictviewformytask, $daytoparse, $isavailable, 0, $arrayfields, $extrafields); 675 //var_dump($totalforvisibletasks); 676 677 // Show total for all other tasks 678 679 // Calculate total for all tasks 680 $listofdistinctprojectid = array(); // List of all distinct projects 681 if (is_array($tasksarraywithoutfilter) && count($tasksarraywithoutfilter)) 682 { 683 foreach ($tasksarraywithoutfilter as $tmptask) 684 { 685 $listofdistinctprojectid[$tmptask->fk_project] = $tmptask->fk_project; 686 } 687 } 688 //var_dump($listofdistinctprojectid); 689 $totalforeachday = array(); 690 foreach ($listofdistinctprojectid as $tmpprojectid) 691 { 692 $projectstatic->id = $tmpprojectid; 693 $projectstatic->loadTimeSpent($daytoparse, 0, $usertoprocess->id); // Load time spent from table projet_task_time for the project into this->weekWorkLoad and this->weekWorkLoadPerTask for all days of a week 694 for ($idw = 0; $idw < 7; $idw++) 695 { 696 $tmpday = dol_time_plus_duree($daytoparse, $idw, 'd'); 697 $totalforeachday[$tmpday] += $projectstatic->weekWorkLoad[$tmpday]; 698 } 699 } 700 //var_dump($totalforeachday); 701 702 // Is there a diff between selected/filtered tasks and all tasks ? 703 $isdiff = 0; 704 if (count($totalforeachday)) 705 { 706 $timeonothertasks = ($totalforeachday[$daytoparse] - $totalforvisibletasks[$daytoparse]); 707 if ($timeonothertasks) 708 { 709 $isdiff = 1; 710 } 711 } 712 713 // There is a diff between total shown on screen and total spent by user, so we add a line with all other cumulated time of user 714 if ($isdiff) 715 { 716 print '<tr class="oddeven othertaskwithtime">'; 717 print '<td colspan="'.($colspan - 1).'" class="opacitymedium">'; 718 print $langs->trans("OtherFilteredTasks"); 719 print '</td>'; 720 print '<td class="leftborder"></td>'; 721 print '<td class="center">'; 722 $timeonothertasks = ($totalforeachday[$daytoparse] - $totalforvisibletasks[$daytoparse]); 723 //if ($timeonothertasks) 724 //{ 725 print '<span class="timesheetalreadyrecorded" title="texttoreplace"><input type="text" class="center" size="2" disabled="" id="timespent[-1][0]" name="task[-1][0]" value="'; 726 if ($timeonothertasks) print convertSecondToTime($timeonothertasks, 'allhourmin'); 727 print '"></span>'; 728 //} 729 print '</td>'; 730 print ' <td class="liste_total"></td>'; 731 print ' <td class="liste_total"></td>'; 732 print '</tr>'; 733 } 734 735 if ($conf->use_javascript_ajax) 736 { 737 print '<tr class="liste_total">'; 738 print '<td class="liste_total" colspan="'.($colspan - 1 + $addcolspan).'">'; 739 print $langs->trans("Total"); 740 print '</td>'; 741 print '<td class="liste_total leftborder">'; 742 //print ' - '.$langs->trans("ExpectedWorkedHours").': <strong>'.price($usertoprocess->weeklyhours, 1, $langs, 0, 0).'</strong>'; 743 print '</td>'; 744 745 print '<td class="liste_total center'.($cssonholiday ? ' '.$cssonholiday : '').($cssweekend ? ' '.$cssweekend : '').'"><div class="totalDay0"> </div></td>'; 746 747 print '<td class="liste_total"></td> 748 <td class="liste_total"></td> 749 </tr>'; 750 } 751} else { 752 print '<tr><td colspan="14"><span class="opacitymedium">'.$langs->trans("NoAssignedTasks").'</span></td></tr>'; 753} 754print "</table>"; 755print '</div>'; 756 757print '<input type="hidden" id="numberOfLines" name="numberOfLines" value="'.count($tasksarray).'"/>'."\n"; 758 759print '<div class="center">'; 760print '<input type="submit" class="button button-save"'.($disabledtask ? ' disabled' : '').' value="'.$langs->trans("Save").'">'; 761print '</div>'; 762 763print '</form>'; 764 765$modeinput = 'hours'; 766 767if ($conf->use_javascript_ajax) 768{ 769 print "\n<!-- JS CODE TO ENABLE Tooltips on all object with class classfortooltip -->\n"; 770 print '<script type="text/javascript">'."\n"; 771 print "jQuery(document).ready(function () {\n"; 772 print ' jQuery(".timesheetalreadyrecorded").tooltip({ 773 show: { collision: "flipfit", effect:\'toggle\', delay:50 }, 774 hide: { effect:\'toggle\', delay: 50 }, 775 tooltipClass: "mytooltip", 776 content: function () { 777 return \''.dol_escape_js($langs->trans("TimeAlreadyRecorded", $usertoprocess->getFullName($langs))).'\'; 778 } 779 });'."\n"; 780 781 print ' updateTotal(0,\''.$modeinput.'\');'; 782 print "\n});\n"; 783 print '</script>'; 784} 785 786// End of page 787llxFooter(); 788$db->close(); 789