dolibarr 22.0.5
perday.php
Go to the documentation of this file.
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-2025 Frédéric France <frederic.france@free.fr>
7 * Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 */
22
29require "../../main.inc.php";
30require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
31require_once DOL_DOCUMENT_ROOT.'/projet/class/task.class.php';
32require_once DOL_DOCUMENT_ROOT.'/core/lib/project.lib.php';
33require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
34require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php';
35require_once DOL_DOCUMENT_ROOT.'/core/class/html.formprojet.class.php';
36require_once DOL_DOCUMENT_ROOT.'/core/class/html.formcompany.class.php';
37require_once DOL_DOCUMENT_ROOT.'/holiday/class/holiday.class.php';
38
48// Load translation files required by the page
49$langs->loadLangs(array('projects', 'users', 'companies'));
50
51$action = GETPOST('action', 'aZ09');
52$mode = GETPOST("mode", 'alpha');
53$id = GETPOSTINT('id');
54$taskid = GETPOSTINT('taskid');
55
56$contextpage = GETPOST('contextpage', 'aZ') ? GETPOST('contextpage', 'aZ') : 'timespent';
57
58$mine = 0;
59if ($mode == 'mine') {
60 $mine = 1;
61}
62
63$projectid = GETPOSTISSET("id") ? GETPOSTINT("id", 1) : GETPOSTINT("projectid");
64
65$hookmanager->initHooks(array('timesheetperdaycard'));
66
67// Security check
68$socid = 0;
69// For external user, no check is done on company because readability is managed by public status of project and assignment.
70//if ($user->socid > 0) $socid=$user->socid;
71$result = restrictedArea($user, 'projet', $projectid);
72
73$now = dol_now();
74
75$year = GETPOSTINT('reyear') ? GETPOSTINT('reyear') : (GETPOSTINT("year") ? GETPOSTINT("year") : (GETPOSTINT("addtimeyear") ? GETPOSTINT("addtimeyear") : date("Y")));
76$month = GETPOSTINT('remonth') ? GETPOSTINT('remonth') : (GETPOSTINT("month") ? GETPOSTINT("month") : (GETPOSTINT("addtimemonth") ? GETPOSTINT("addtimemonth") : date("m")));
77$day = GETPOSTINT('reday') ? GETPOSTINT('reday') : (GETPOSTINT("day") ? GETPOSTINT("day") : (GETPOSTINT("addtimeday") ? GETPOSTINT("addtimeday") : date("d")));
78$week = GETPOSTINT("week") ? GETPOSTINT("week") : date("W");
79
80$day = (int) $day;
81
82//$search_categ = GETPOST("search_categ", 'alpha');
83$search_usertoprocessid = GETPOSTINT('search_usertoprocessid');
84$search_task_ref = GETPOST('search_task_ref', 'alpha');
85$search_task_label = GETPOST('search_task_label', 'alpha');
86$search_project_ref = GETPOST('search_project_ref', 'alpha');
87$search_thirdparty = GETPOST('search_thirdparty', 'alpha');
88$search_declared_progress = GETPOST('search_declared_progress', 'alpha');
89
90$sortfield = GETPOST('sortfield', 'aZ09comma');
91$sortorder = GETPOST('sortorder', 'aZ09comma');
92
93$monthofday = GETPOSTINT('addtimemonth');
94$dayofday = GETPOSTINT('addtimeday');
95$yearofday = GETPOSTINT('addtimeyear');
96
97//var_dump(GETPOST('remonth'));
98//var_dump(GETPOST('button_search_x'));
99//var_dump(GETPOST('button_addtime'));
100
101$daytoparse = $now;
102if ($year && $month && $day) {
103 $daytoparse = dol_mktime(0, 0, 0, $month, $day, $year); // this are value submitted after submit of action 'submitdateselect'
104} elseif ($yearofday && $monthofday && $dayofday) {
105 $daytoparse = dol_mktime(0, 0, 0, $monthofday, $dayofday, $yearofday); // xxxofday is value of day after submit action 'addtime'
106}
107
108$daytoparsegmt = dol_now('gmt');
109if ($yearofday && $monthofday && $dayofday) {
110 $daytoparsegmt = dol_mktime(0, 0, 0, $monthofday, $dayofday, $yearofday, 'gmt');
111} elseif ($year && $month && $day) { // xxxofday is value of day after submit action 'addtime'
112 $daytoparsegmt = dol_mktime(0, 0, 0, $month, $day, $year, 'gmt');
113} // this are value submitted after submit of action 'submitdateselect'
114
115if (empty($search_usertoprocessid) || $search_usertoprocessid == $user->id) {
116 $usertoprocess = $user;
117 $search_usertoprocessid = $usertoprocess->id;
118} elseif ($search_usertoprocessid > 0) {
119 $usertoprocess = new User($db);
120 $usertoprocess->fetch($search_usertoprocessid);
121 $search_usertoprocessid = $usertoprocess->id;
122} else {
123 $usertoprocess = new User($db);
124}
125
126$object = new Task($db);
127$project = new Project($db);
128
129// Extra fields
130$extrafields = new ExtraFields($db);
131
132// fetch optionals attributes and labels
133$extrafields->fetch_name_optionals_label($object->table_element);
134
135// Definition of fields for list
136$arrayfields = array();
137$arrayfields['t.planned_workload'] = array('label' => 'PlannedWorkload', 'checked' => '1', 'enabled' => '1', 'position' => 0);
138$arrayfields['t.progress'] = array('label' => 'ProgressDeclared', 'checked' => '1', 'enabled' => '1', 'position' => 0);
139$arrayfields['timeconsumed'] = array('label' => 'TimeConsumed', 'checked' => '1', 'enabled' => '1', 'position' => 15);
140/*$arrayfields=array(
141 // Project
142 'p.opp_amount'=>array('label'=>$langs->trans("OpportunityAmountShort"), 'checked'=>0, 'enabled'=>($conf->global->PROJECT_USE_OPPORTUNITIES?1:0), 'position'=>103),
143 'p.fk_opp_status'=>array('label'=>$langs->trans("OpportunityStatusShort"), 'checked'=>0, 'enabled'=>($conf->global->PROJECT_USE_OPPORTUNITIES?1:0), 'position'=>104),
144 'p.opp_percent'=>array('label'=>$langs->trans("OpportunityProbabilityShort"), 'checked'=>0, 'enabled'=>($conf->global->PROJECT_USE_OPPORTUNITIES?1:0), 'position'=>105),
145 'p.budget_amount'=>array('label'=>$langs->trans("Budget"), 'checked'=>0, 'position'=>110),
146 'p.usage_bill_time'=>array('label'=>$langs->trans("BillTimeShort"), 'checked'=>0, 'position'=>115),
147 );
148 */
149// Extra fields
150$extrafieldsobjectkey = $object->table_element;
151$extrafieldsobjectprefix = 'efpt.';
152include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_array_fields.tpl.php';
153
154$arrayfields = dol_sort_array($arrayfields, 'position');
155
156
157$search_array_options_project = $extrafields->getOptionalsFromPost($project->table_element, '', 'search_');
158$search_array_options_task = $extrafields->getOptionalsFromPost($object->table_element, '', 'search_task_');
159
160
161/*
162 * Actions
163 */
164$error = 0;
165$parameters = array('id' => $id, 'taskid' => $taskid, 'projectid' => $projectid);
166$reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
167if ($reshook < 0) {
168 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
169}
170// Purge criteria
171if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter.x', 'alpha') || GETPOST('button_removefilter', 'alpha')) { // All tests are required to be compatible with all browsers
172 $action = '';
173 //$search_categ = '';
174 $search_usertoprocessid = $user->id;
175 $search_task_ref = '';
176 $search_task_label = '';
177 $search_project_ref = '';
178 $search_thirdparty = '';
179 $search_declared_progress = '';
180
181 $search_array_options_project = array();
182 $search_array_options_task = array();
183
184 // We redefine $usertoprocess
185 $usertoprocess = $user;
186}
187if (GETPOST("button_search_x", 'alpha') || GETPOST("button_search.x", 'alpha') || GETPOST("button_search", 'alpha')) {
188 $action = '';
189}
190
191if (GETPOST('submitdateselect')) {
192 if (GETPOSTINT('remonth') && GETPOSTINT('reday') && GETPOSTINT('reyear')) {
193 $daytoparse = dol_mktime(0, 0, 0, GETPOSTINT('remonth'), GETPOSTINT('reday'), GETPOSTINT('reyear'));
194 }
195
196 $action = '';
197}
198
199include DOL_DOCUMENT_ROOT.'/core/actions_changeselectedfields.inc.php';
200
201if ($action == 'addtime' && $user->hasRight('projet', 'lire') && GETPOST('assigntask') && GETPOST('formfilteraction') != 'listafterchangingselectedfields') {
202 $action = 'assigntask';
203
204 if ($taskid > 0) {
205 $result = $object->fetch($taskid);
206 if ($result < 0) {
207 $error++;
208 }
209 } else {
210 setEventMessages($langs->transnoentitiesnoconv("ErrorFieldRequired", $langs->transnoentitiesnoconv("Task")), null, 'errors');
211 $error++;
212 }
213 if (!GETPOST('type')) {
214 setEventMessages($langs->transnoentitiesnoconv("ErrorFieldRequired", $langs->transnoentitiesnoconv("Type")), null, 'errors');
215 $error++;
216 }
217 if (!$error) {
218 $idfortaskuser = $usertoprocess->id;
219 $result = $object->add_contact($idfortaskuser, GETPOST("type"), 'internal');
220
221 if ($result >= 0 || $result == -2) { // Contact add ok or already contact of task
222 // 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)
223 $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';
224 $sql .= ' AND ec.fk_socpeople = '.((int) $idfortaskuser)." AND ec.element_id = ".((int) $object->fk_project)." AND tc.element = 'project' AND source = 'internal'";
225 $resql = $db->query($sql);
226 if ($resql) {
227 $obj = $db->fetch_object($resql);
228 if (!$obj) { // User is not already linked to project, so we will create link to first type
229 $project = new Project($db);
230 $project->fetch($object->fk_project);
231 // Get type
232 $listofprojcontact = $project->liste_type_contact('internal');
233
234 if (count($listofprojcontact)) {
235 $tmparray = array_keys($listofprojcontact);
236 $typeforprojectcontact = reset($tmparray);
237 $result = $project->add_contact($idfortaskuser, $typeforprojectcontact, 'internal');
238 }
239 }
240 } else {
241 dol_print_error($db);
242 }
243 }
244 }
245
246 if ($result < 0) {
247 $error++;
248 if ($object->error == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
249 $langs->load("errors");
250 setEventMessages($langs->trans("ErrorTaskAlreadyAssigned"), null, 'warnings');
251 } else {
252 setEventMessages($object->error, $object->errors, 'errors');
253 }
254 }
255
256 if (!$error) {
257 setEventMessages("TaskAssignedToEnterTime", null);
258 $taskid = 0;
259 }
260
261 $action = '';
262}
263
264if ($action == 'addtime' && $user->hasRight('projet', 'lire') && GETPOST('formfilteraction') != 'listafterchangingselectedfields') {
265 $timespent_duration = array();
266
267 if (is_array($_POST)) {
268 foreach ($_POST as $key => $time) {
269 if (intval($time) > 0) {
270 $matches = array();
271 // Hours or minutes of duration
272 if (preg_match("/([0-9]+)duration(hour|min)/", $key, $matches)) {
273 $id = $matches[1];
274 if ($id > 0) {
275 // We store HOURS in seconds
276 if ($matches[2] == 'hour') {
277 $timespent_duration[$id] += (int) $time * 60 * 60;
278 }
279
280 // We store MINUTES in seconds
281 if ($matches[2] == 'min') {
282 $timespent_duration[$id] += (int) $time * 60;
283 }
284 }
285 }
286 }
287 }
288 }
289
290 if (count($timespent_duration) > 0) {
291 foreach ($timespent_duration as $key => $val) {
292 $object->fetch($key);
293 $taskid = $object->id;
294
295 if (GETPOSTISSET($taskid.'progress')) {
296 $object->progress = GETPOSTINT($taskid.'progress');
297 } else {
298 unset($object->progress);
299 }
300
301 $object->timespent_duration = $val;
302 $object->timespent_fk_user = $usertoprocess->id;
303 $object->timespent_note = GETPOST($key.'note');
304 if (GETPOSTINT($key."hour") != '' && GETPOSTINT($key."hour") >= 0) { // If hour was entered
305 $object->timespent_datehour = dol_mktime(GETPOSTINT($key."hour"), GETPOSTINT($key."min"), 0, $monthofday, $dayofday, $yearofday);
306 $object->timespent_withhour = 1;
307 } else {
308 $object->timespent_datehour = dol_mktime(12, 0, 0, $monthofday, $dayofday, $yearofday);
309 }
310 $object->timespent_date = $object->timespent_datehour;
311
312 if ($object->timespent_date > 0) {
313 $result = $object->addTimeSpent($user);
314 } else {
315 setEventMessages("ErrorBadDate", null, 'errors');
316 $error++;
317 break;
318 }
319
320 if ($result < 0) {
321 setEventMessages($object->error, $object->errors, 'errors');
322 $error++;
323 break;
324 }
325 }
326
327 if (!$error) {
328 setEventMessages($langs->trans("RecordSaved"), null, 'mesgs');
329
330 // Redirect to avoid submit twice on back
331 header('Location: '.$_SERVER["PHP_SELF"].'?'.($projectid ? 'id='.$projectid : '').($search_usertoprocessid ? '&search_usertoprocessid='.urlencode((string) $search_usertoprocessid) : '').($mode ? '&mode='.$mode : '').'&year='.$yearofday.'&month='.$monthofday.'&day='.$dayofday);
332 exit;
333 }
334 } else {
335 setEventMessages($langs->trans("ErrorTimeSpentIsEmpty"), null, 'errors');
336 }
337}
338
339
340
341/*
342 * View
343 */
344
345$form = new Form($db);
346$formother = new FormOther($db);
347$formcompany = new FormCompany($db);
348$formproject = new FormProjets($db);
349$projectstatic = new Project($db);
350$project = new Project($db);
351$taskstatic = new Task($db);
352$thirdpartystatic = new Societe($db);
353$holiday = new Holiday($db);
354
355$prev = dol_getdate($daytoparse - (24 * 3600));
356$prev_year = $prev['year'];
357$prev_month = $prev['mon'];
358$prev_day = $prev['mday'];
359
360$next = dol_getdate($daytoparse + (24 * 3600));
361$next_year = $next['year'];
362$next_month = $next['mon'];
363$next_day = $next['mday'];
364
365$title = $langs->trans("TimeSpent");
366
367$projectsListId = $projectstatic->getProjectsAuthorizedForUser($usertoprocess, (empty($usertoprocess->id) ? 2 : 0), 1); // Return all project i have permission on (assigned to me+public). I want my tasks and some of my task may be on a public projet that is not my project
368
369if ($id) {
370 $project->fetch($id);
371 $project->fetch_thirdparty();
372}
373
374$onlyopenedproject = 1; // or -1
375$morewherefilter = '';
376
377if ($search_project_ref) {
378 $morewherefilter .= natural_search(array("p.ref", "p.title"), $search_project_ref);
379}
380if ($search_task_ref) {
381 $morewherefilter .= natural_search("t.ref", $search_task_ref);
382}
383if ($search_task_label) {
384 $morewherefilter .= natural_search(array("t.ref", "t.label"), $search_task_label);
385}
386if ($search_thirdparty) {
387 $morewherefilter .= natural_search("s.nom", $search_thirdparty);
388}
389if ($search_declared_progress) {
390 $morewherefilter .= natural_search("t.progress", $search_declared_progress, 1);
391}
392
393$sql = &$morewherefilter;
394
395/*$search_array_options = $search_array_options_project;
396$extrafieldsobjectprefix='efp.';
397$search_options_pattern='search_options_';
398$extrafieldsobjectkey='projet';
399include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_sql.tpl.php';
400*/
401$search_array_options = $search_array_options_task;
402$extrafieldsobjectprefix = 'efpt.';
403$search_options_pattern = 'search_task_options_';
404$extrafieldsobjectkey = 'projet_task';
405include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_sql.tpl.php';
406
407$tasksarray = $taskstatic->getTasksArray(null, null, ($project->id ? $project->id : 0), $socid, 0, $search_project_ref, (string) $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.
408
409$tasksarraywithoutfilter = array();
410if ($morewherefilter) { // Get all task without any filter, so we can show total of time spent for not visible tasks
411 $tasksarraywithoutfilter = $taskstatic->getTasksArray(null, null, ($project->id ? $project->id : 0), $socid, 0, '', (string) $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.
412}
413$projectsrole = $taskstatic->getUserRolesForProjectsOrTasks($usertoprocess, null, ($project->id ? (string) $project->id : '0'), 0, $onlyopenedproject);
414$tasksrole = $taskstatic->getUserRolesForProjectsOrTasks(null, $usertoprocess, ($project->id ? (string) $project->id : '0'), 0, $onlyopenedproject);
415
416llxHeader('', $title, '', '', 0, 0, array('/core/js/timesheet.js'), '', '', 'mod-project project-activity page-activity_perday');
417
418//print_barre_liste($title, $page, $_SERVER["PHP_SELF"], "", $sortfield, $sortorder, "", $num, '', 'project');
419
420$param = '';
421$param .= ($mode ? '&mode='.urlencode($mode) : '');
422$param .= ($search_project_ref ? '&search_project_ref='.urlencode($search_project_ref) : '');
423$param .= ($search_usertoprocessid > 0 ? '&search_usertoprocessid='.urlencode((string) $search_usertoprocessid) : '');
424$param .= ($search_thirdparty ? '&search_thirdparty='.urlencode($search_thirdparty) : '');
425$param .= ($search_task_ref ? '&search_task_ref='.urlencode($search_task_ref) : '');
426$param .= ($search_task_label ? '&search_task_label='.urlencode($search_task_label) : '');
427
428/*
429$search_array_options = $search_array_options_project;
430$search_options_pattern='search_options_';
431include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_param.tpl.php';
432*/
433
434$search_array_options = $search_array_options_task;
435$search_options_pattern = 'search_task_options_';
436include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_param.tpl.php';
437
438// Show navigation bar
439$nav = '<a class="inline-block valignmiddle" href="?year='.$prev_year."&month=".$prev_month."&day=".$prev_day.$param.'">'.img_previous($langs->trans("Previous"))."</a>\n";
440$nav .= dol_print_date(dol_mktime(0, 0, 0, $month, $day, $year), "%a").' ';
441$nav .= ' <span id="month_name">'.dol_print_date(dol_mktime(0, 0, 0, $month, $day, $year), "day")." </span>\n";
442$nav .= '<a class="inline-block valignmiddle" href="?year='.$next_year."&month=".$next_month."&day=".$next_day.$param.'">'.img_next($langs->trans("Next"))."</a>\n";
443$nav .= ' '.$form->selectDate(-1, '', 0, 0, 2, "addtime", 1, ($conf->dol_optimize_smallscreen ? 0 : 1)).' ';
444$nav .= ' <button type="submit" name="button_search_x" value="x" class="bordertransp nobordertransp button_search"><span class="fa fa-search"></span></button>';
445
446$picto = 'clock';
447
448print '<form name="addtime" method="POST" action="'.$_SERVER["PHP_SELF"].($project->id > 0 ? '?id='.$project->id : '').'">';
449print '<input type="hidden" name="token" value="'.newToken().'">';
450print '<input type="hidden" name="action" value="addtime">';
451print '<input type="hidden" name="formfilteraction" id="formfilteraction" value="list">';
452print '<input type="hidden" name="contextpage" value="'.$contextpage.'">';
453print '<input type="hidden" name="mode" value="'.$mode.'">';
454$tmp = dol_getdate($daytoparse);
455print '<input type="hidden" name="addtimeyear" value="'.$tmp['year'].'">';
456print '<input type="hidden" name="addtimemonth" value="'.$tmp['mon'].'">';
457print '<input type="hidden" name="addtimeday" value="'.$tmp['mday'].'">';
458
459$head = project_timesheet_prepare_head($mode, $usertoprocess);
460print dol_get_fiche_head($head, 'inputperday', $langs->trans('TimeSpent'), -1, $picto);
461
462// Show description of content
463print '<div class="hideonsmartphone opacitymedium">';
464if ($mine || ($usertoprocess->id == $user->id)) {
465 print $langs->trans("MyTasksDesc").'.'.($onlyopenedproject ? ' '.$langs->trans("OnlyOpenedProject") : '').'<br>';
466} else {
467 if (empty($usertoprocess->id) || $usertoprocess->id < 0) {
468 if ($user->hasRight('projet', 'all', 'lire') && !$socid) {
469 print $langs->trans("ProjectsDesc").'.'.($onlyopenedproject ? ' '.$langs->trans("OnlyOpenedProject") : '').'<br>';
470 } else {
471 print $langs->trans("ProjectsPublicTaskDesc").'.'.($onlyopenedproject ? ' '.$langs->trans("OnlyOpenedProject") : '').'<br>';
472 }
473 }
474}
475if ($mine || ($usertoprocess->id == $user->id)) {
476 print $langs->trans("OnlyYourTaskAreVisible").'<br>';
477} else {
478 print $langs->trans("AllTaskVisibleButEditIfYouAreAssigned").'<br>';
479}
480print '</div>';
481
482print dol_get_fiche_end();
483
484
485print '<div class="'.($conf->dol_optimize_smallscreen ? 'center centpercent' : 'floatright right').'">'.$nav.'</div>'; // We move this before the assign to components so, the default submit button is not the assign to.
486
487print '<div class="colorbacktimesheet valignmiddle'.($conf->dol_optimize_smallscreen ? ' center' : ' float').'">';
488$titleassigntask = $langs->transnoentities("AssignTaskToMe");
489if ($usertoprocess->id != $user->id) {
490 $titleassigntask = $langs->transnoentities("AssignTaskToUser", $usertoprocess->getFullName($langs));
491}
492print '<div class="taskiddiv inline-block">';
493print img_picto('', 'projecttask', 'class="pictofixedwidth"');
494$formproject->selectTasks($socid ? $socid : -1, $taskid, 'taskid', 32, 0, '-- '.$langs->trans("ChooseANotYetAssignedTask").' --', 1, 0, 0, 'widthcentpercentminusx', '', 'all', $usertoprocess);
495print '</div>';
496print ' ';
497print $formcompany->selectTypeContact($object, '', 'type', 'internal', 'position', 0, 'maxwidth150onsmartphone');
498print '<input type="submit" class="button valignmiddle smallonsmartphone small" name="assigntask" value="'.dol_escape_htmltag($titleassigntask).'">';
499print '</div>';
500
501print '<div class="clearboth" style="padding-bottom: 20px;"></div>';
502
503
504$moreforfilter = '';
505
506// Filter on categories
507/*if (isModEnabled("categorie")) {
508 require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
509 $moreforfilter.='<div class="divsearchfield">';
510 $moreforfilter.=$langs->trans('ProjectCategories'). ': ';
511 $moreforfilter.=$formother->select_categories('project', $search_categ, 'search_categ', 1, 1, 'maxwidth300');
512 $moreforfilter.='</div>';
513}*/
514
515// If the user can view user other than himself
516$includeonly = 'hierarchyme';
517if (!$user->hasRight('user', 'user', 'lire')) {
518 $includeonly = array($user->id);
519}
520$selecteduser = $search_usertoprocessid ? $search_usertoprocessid : $usertoprocess->id;
521$moreforfiltertmp = $form->select_dolusers($selecteduser, 'search_usertoprocessid', 0, null, 0, $includeonly, '', '0', 0, 0, '', 0, '', 'maxwidth200');
522if ($form->num > 1 || empty($conf->dol_optimize_smallscreen)) {
523 $moreforfilter .= '<div class="divsearchfield">';
524 $moreforfilter .= '<div class="inline-block hideonsmartphone"></div>';
525 $moreforfilter .= img_picto($langs->trans('Filter').' '.$langs->trans('User'), 'user', 'class="paddingright pictofixedwidth"');
526 $moreforfilter .= $moreforfiltertmp;
527 $moreforfilter .= '</div>';
528} else {
529 $moreforfilter .= '<input type="hidden" name="search_usertoprocessid" value="'.$selecteduser.'">';
530}
531
532if (!getDolGlobalString('PROJECT_TIMESHEET_DISABLEBREAK_ON_PROJECT')) {
533 $moreforfilter .= '<div class="divsearchfield">';
534 $moreforfilter .= '<div class="inline-block"></div>';
535 $moreforfilter .= img_picto($langs->trans('Filter').' '.$langs->trans('Project'), 'project', 'class="paddingright pictofixedwidth"').'<input type="text" name="search_project_ref" class="maxwidth100" value="'.dol_escape_htmltag($search_project_ref).'">';
536 $moreforfilter .= '</div>';
537
538 $moreforfilter .= '<div class="divsearchfield">';
539 $moreforfilter .= '<div class="inline-block"></div>';
540 $moreforfilter .= img_picto($langs->trans('Filter').' '.$langs->trans('ThirdParty'), 'company', 'class="paddingright pictofixedwidth"').'<input type="text" name="search_thirdparty" class="maxwidth100" value="'.dol_escape_htmltag($search_thirdparty).'">';
541 $moreforfilter .= '</div>';
542}
543
544if (!empty($moreforfilter)) {
545 print '<div class="liste_titre liste_titre_bydiv centpercent">';
546 print $moreforfilter;
547 $parameters = array();
548 $reshook = $hookmanager->executeHooks('printFieldPreListTitle', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
549 print $hookmanager->resPrint;
550 print '</div>';
551}
552
553$varpage = empty($contextpage) ? $_SERVER["PHP_SELF"] : $contextpage;
554$selectedfields = $form->multiSelectArrayWithCheckbox('selectedfields', $arrayfields, $varpage); // This also change content of $arrayfields
555
556// This must be after the $selectedfields
557$addcolspan = 0;
558if (!empty($arrayfields['t.planned_workload']['checked'])) {
559 $addcolspan++;
560}
561if (!empty($arrayfields['t.progress']['checked'])) {
562 $addcolspan++;
563}
564foreach ($arrayfields as $key => $val) {
565 if ($val['checked'] && substr($key, 0, 5) == 'efpt.') {
566 $addcolspan++;
567 }
568}
569
570print '<div class="div-table-responsive">';
571print '<table class="tagtable liste'.($moreforfilter ? " listwithfilterbefore" : "").'" id="tablelines3">'."\n";
572
573print '<tr class="liste_titre_filter">';
574if (getDolGlobalString('PROJECT_TIMESHEET_DISABLEBREAK_ON_PROJECT')) {
575 print '<td class="liste_titre"><input type="text" size="4" name="search_project_ref" value="'.dol_escape_htmltag($search_project_ref).'"></td>';
576}
577if (getDolGlobalString('PROJECT_TIMESHEET_DISABLEBREAK_ON_PROJECT')) {
578 print '<td class="liste_titre"><input type="text" size="4" name="search_thirdparty" value="'.dol_escape_htmltag($search_thirdparty).'"></td>';
579}
580print '<td class="liste_titre"><input type="text" size="4" name="search_task_label" value="'.dol_escape_htmltag($search_task_label).'"></td>';
581// TASK fields
582$search_options_pattern = 'search_task_options_';
583$extrafieldsobjectkey = 'projet_task';
584$extrafieldsobjectprefix = 'efpt.';
585include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_input.tpl.php';
586if (!empty($arrayfields['t.planned_workload']['checked'])) {
587 print '<td class="liste_titre"></td>';
588}
589if (!empty($arrayfields['t.progress']['checked'])) {
590 print '<td class="liste_titre right"><input type="text" size="4" name="search_declared_progress" value="'.dol_escape_htmltag($search_declared_progress).'"></td>';
591}
592if (!empty($arrayfields['timeconsumed']['checked'])) {
593 print '<td class="liste_titre"></td>';
594 print '<td class="liste_titre"></td>';
595}
596print '<td class="liste_titre"></td>';
597print '<td class="liste_titre"></td>';
598print '<td class="liste_titre"></td>';
599// Action column
600print '<td class="liste_titre nowrap right">';
601$searchpicto = $form->showFilterAndCheckAddButtons(0);
602print $searchpicto;
603print '</td>';
604print "</tr>\n";
605
606print '<tr class="liste_titre">';
607if (getDolGlobalString('PROJECT_TIMESHEET_DISABLEBREAK_ON_PROJECT')) {
608 print '<th>'.$langs->trans("Project").'</th>';
609}
610if (getDolGlobalString('PROJECT_TIMESHEET_DISABLEBREAK_ON_PROJECT')) {
611 print '<th>'.$langs->trans("ThirdParty").'</th>';
612}
613print '<th>'.$langs->trans("Task").'</th>';
614// TASK fields
615$extrafieldsobjectkey = 'projet_task';
616$extrafieldsobjectprefix = 'efpt.';
617include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_title.tpl.php';
618if (!empty($arrayfields['t.planned_workload']['checked'])) {
619 print '<th class="leftborder plannedworkload minwidth75 maxwidth100 right" title="'.dol_escape_htmltag($langs->trans("PlannedWorkload")).'">'.$form->textwithpicto($langs->trans("PlannedWorkloadShort"), $langs->trans("PlannedWorkload")).'</th>';
620}
621if (!empty($arrayfields['t.progress']['checked'])) {
622 print '<th class="right minwidth75 maxwidth100 title="'.dol_escape_htmltag($langs->trans("ProgressDeclared")).'">'.$langs->trans("ProgressDeclared").'</th>';
623}
624if (!empty($arrayfields['timeconsumed']['checked'])) {
625 print '<th class="right maxwidth100">'.$langs->trans("TimeSpentSmall").'<br>';
626 print '<span class="nowraponall">';
627 print '<span class="opacitymedium nopadding userimg"><img alt="Photo" class="photouserphoto userphoto" src="'.DOL_URL_ROOT.'/theme/common/everybody.png"></span>';
628 print '<span class="opacitymedium paddingleft">'.$langs->trans("EverybodySmall").'</span>';
629 print '</span>';
630 print '</th>';
631 print '<th class="right maxwidth75 maxwidth100">'.$langs->trans("TimeSpentSmall").($usertoprocess->firstname ? '<br><span class="nowraponall">'.$usertoprocess->getNomUrl(-2).'<span class="opacitymedium paddingleft">'.dol_trunc($usertoprocess->firstname, 10).'</span></span>' : '').'</th>';
632}
633print '<th class="center leftborder">'.$langs->trans("HourStart").'</td>';
634
635// By default, we can edit only tasks we are assigned to
636$restrictviewformytask = getDolGlobalInt('PROJECT_TIME_SHOW_TASK_NOT_ASSIGNED', 2);
637
638$numendworkingday = 0;
639$numstartworkingday = 0;
640// Get if user is available or not for each day
641$isavailable = array();
642
643// Assume from Monday to Friday if conf empty or badly formed
644$numstartworkingday = 1;
645$numendworkingday = 5;
646
647if (getDolGlobalString('MAIN_DEFAULT_WORKING_DAYS')) {
648 $tmparray = explode('-', getDolGlobalString('MAIN_DEFAULT_WORKING_DAYS'));
649 if (count($tmparray) >= 2) {
650 $numstartworkingday = $tmparray[0];
651 $numendworkingday = $tmparray[1];
652 }
653}
654
655$statusofholidaytocheck = Holiday::STATUS_APPROVED;
656$isavailablefordayanduser = $holiday->verifDateHolidayForTimestamp($usertoprocess->id, $daytoparse, (string) $statusofholidaytocheck); // $daytoparse is a date with hours = 0
657$isavailable[$daytoparse] = $isavailablefordayanduser; // in projectLinesPerWeek later, we are using $firstdaytoshow and dol_time_plus_duree to loop on each day
658
659$test = num_public_holiday($daytoparsegmt, $daytoparsegmt + 86400, $mysoc->country_code);
660if ($test) {
661 $isavailable[$daytoparse] = array('morning' => false, 'afternoon' => false, 'morning_reason' => 'public_holiday', 'afternoon_reason' => 'public_holiday');
662}
663
664$tmparray = dol_getdate($daytoparse, true); // detail of current day
665// For monday, must be 0 for monday if MAIN_START_WEEK = 1, must be 1 for monday if MAIN_START_WEEK = 0
666$idw = ($tmparray['wday'] - (!getDolGlobalString('MAIN_START_WEEK') ? 0 : 1));
667// 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)
668$cssweekend = '';
669if ((($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.
670 $cssweekend = 'weekend';
671}
672
673$tmpday = dol_time_plus_duree($daytoparse, $idw, 'd');
674
675$cssonholiday = '';
676if (!$isavailable[$daytoparse]['morning'] && !$isavailable[$daytoparse]['afternoon']) {
677 $cssonholiday .= 'onholidayallday ';
678} elseif (!$isavailable[$daytoparse]['morning']) {
679 $cssonholiday .= 'onholidaymorning ';
680} elseif (!$isavailable[$daytoparse]['afternoon']) {
681 $cssonholiday .= 'onholidayafternoon ';
682}
683
684print '<th class="center'.($cssonholiday ? ' '.$cssonholiday : '').($cssweekend ? ' '.$cssweekend : '').'">'.$langs->trans("Duration").'</th>';
685print '<th class="center">'.$langs->trans("Note").'</th>';
686//print '<td class="center"></td>';
687print_liste_field_titre($selectedfields, $_SERVER["PHP_SELF"], "", '', '', '', $sortfield, $sortorder, 'center maxwidthsearch ');
688
689print "</tr>\n";
690
691$colspan = 2 + (!getDolGlobalString('PROJECT_TIMESHEET_DISABLEBREAK_ON_PROJECT') ? 0 : 2);
692
693if ($conf->use_javascript_ajax && count($tasksarray) >= getDolGlobalInt('NBLINES_TO_DUPLICATE_TOTAL_TIMESPENT_ON_TOP', 10)) {
694 print '<tr class="liste_total hideonsmartphone">';
695 print '<td class="liste_total" colspan="'.($colspan - 1 + $addcolspan).'">';
696 print $langs->trans("Total");
697 print '</td>';
698 if (!empty($arrayfields['timeconsumed']['checked'])) {
699 print '<td class="liste_total"></td>';
700 print '<td class="liste_total"></td>';
701 }
702 print '<td class="liste_total leftborder">';
703 //print ' - '.$langs->trans("ExpectedWorkedHours").': <strong>'.price($usertoprocess->weeklyhours, 1, $langs, 0, 0).'</strong>';
704 print '</td>';
705
706 print '<td class="liste_total center'.($cssonholiday ? ' '.$cssonholiday : '').($cssweekend ? ' '.$cssweekend : '').'"><div class="totalDay0">&nbsp;</div></td>';
707
708 print '<td class="liste_total"></td>';
709 print '<td class="liste_total"></td>';
710 print '</tr>';
711}
712
713
714if (count($tasksarray) > 0) {
715 //var_dump($tasksarray); // contains only selected tasks
716 //var_dump($tasksarraywithoutfilter); // contains all tasks (if there is a filter, not defined if no filter)
717 //var_dump($tasksrole);
718
719 $j = 0;
720 $level = 0;
721 $totalforvisibletasks = projectLinesPerDay($j, 0, $usertoprocess, $tasksarray, $level, $projectsrole, $tasksrole, $mine, $restrictviewformytask, $daytoparse, $isavailable, 0, $arrayfields, $extrafields);
722 //var_dump($totalforvisibletasks);
723
724 // Show total for all other tasks
725
726 // Calculate total for all tasks
727 $listofdistinctprojectid = array(); // List of all distinct projects
728 if (!empty($tasksarraywithoutfilter) && is_array($tasksarraywithoutfilter) && count($tasksarraywithoutfilter)) {
729 foreach ($tasksarraywithoutfilter as $tmptask) {
730 $listofdistinctprojectid[$tmptask->fk_project] = $tmptask->fk_project;
731 }
732 }
733 //var_dump($listofdistinctprojectid);
734 $totalforeachday = array();
735 foreach ($listofdistinctprojectid as $tmpprojectid) {
736 $projectstatic->id = $tmpprojectid;
737 $projectstatic->loadTimeSpent($daytoparse, 0, $usertoprocess->id); // Load time spent from table element_time for the project into this->weekWorkLoad and this->weekWorkLoadPerTask for all days of a week
738 for ($idw = 0; $idw < 7; $idw++) {
739 $tmpday = dol_time_plus_duree($daytoparse, $idw, 'd');
740 $totalforeachday[$tmpday] += $projectstatic->weekWorkLoad[$tmpday];
741 }
742 }
743 //var_dump($totalforeachday);
744
745 // Is there a diff between selected/filtered tasks and all tasks ?
746 $isdiff = 0;
747 if (count($totalforeachday)) {
748 $timeonothertasks = ($totalforeachday[$daytoparse] - $totalforvisibletasks[$daytoparse]);
749 if ($timeonothertasks) {
750 $isdiff = 1;
751 }
752 }
753
754 // 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
755 if ($isdiff) {
756 print '<tr class="oddeven othertaskwithtime">';
757 print '<td colspan="'.($colspan - 1).'" class="opacitymedium">';
758 print $langs->trans("OtherFilteredTasks");
759 print '</td>';
760 if (!empty($arrayfields['timeconsumed']['checked'])) {
761 print '<td class="liste_total"></td>';
762 print '<td class="liste_total"></td>';
763 }
764 print '<td class="leftborder"></td>';
765 print '<td class="right">';
766 $timeonothertasks = ($totalforeachday[$daytoparse] - $totalforvisibletasks[$daytoparse]);
767 //if ($timeonothertasks)
768 //{
769 print '<span class="timesheetalreadyrecorded" title="texttoreplace"><input type="text" class="center width40" disabled="" id="timespent[-1][0]" name="task[-1][0]" value="';
770 if ($timeonothertasks) {
771 print convertSecondToTime($timeonothertasks, 'allhourmin');
772 }
773 print '"></span>';
774 //}
775 print '</td>';
776 print ' <td class="liste_total borderleft"></td>';
777 print ' <td class="liste_total"></td>';
778 print '</tr>';
779 }
780
781 if ($conf->use_javascript_ajax) {
782 print '<tr class="liste_total">';
783 print '<td class="liste_total" colspan="'.($colspan - 1 + $addcolspan).'">';
784 print $langs->trans("Total");
785 print '</td>';
786 if (!empty($arrayfields['timeconsumed']['checked'])) {
787 print '<td class="liste_total"></td>';
788 print '<td class="liste_total"></td>';
789 }
790 print '<td class="liste_total leftborder borderleft">';
791 //print ' - '.$langs->trans("ExpectedWorkedHours").': <strong>'.price($usertoprocess->weeklyhours, 1, $langs, 0, 0).'</strong>';
792 print '</td>';
793
794 print '<td class="liste_total center'.($cssonholiday ? ' '.$cssonholiday : '').($cssweekend ? ' '.$cssweekend : '').'"><div class="totalDay0">&nbsp;</div></td>';
795
796 print '<td class="liste_total"></td>
797 <td class="liste_total"></td>
798 </tr>';
799 }
800} else {
801 print '<tr><td colspan="14"><span class="opacitymedium">'.$langs->trans("NoAssignedTasks").'</span></td></tr>';
802}
803print "</table>";
804print '</div>';
805
806print '<input type="hidden" id="numberOfLines" name="numberOfLines" value="'.count($tasksarray).'"/>'."\n";
807
808print '<div class="center">';
809print '<input type="submit" name="button_addtime" class="button button-save"'.(!empty($disabledtask) ? ' disabled' : '').' value="'.$langs->trans("Save").'">';
810print '</div>';
811
812print '</form>';
813
814if (!empty($conf->use_javascript_ajax)) {
815 $modeinput = 'hours';
816 print "\n<!-- JS CODE TO ENABLE Tooltips on all object with class classfortooltip -->\n";
817 print '<script type="text/javascript">'."\n";
818 print "jQuery(document).ready(function () {\n";
819 print " updateTotal(0, '".dol_escape_js($modeinput)."');\n";
820 print ' jQuery(".timesheetalreadyrecorded").tooltip({
821 show: { collision: "flipfit", effect:\'toggle\', delay:50 },
822 hide: { effect:\'toggle\', delay: 50 },
823 tooltipClass: "mytooltip",
824 content: function () {
825 return \''.dol_escape_js($langs->trans("TimeAlreadyRecorded", $usertoprocess->getFullName($langs))).'\';
826 }
827 });'."\n";
828 print " jQuery('.inputhour, .inputminute').bind('keyup', function(e) { updateTotal(0, '".dol_escape_js($modeinput)."') });";
829 print "\n});\n";
830 print '</script>';
831}
832
833// End of page
834llxFooter();
835$db->close();
$id
Support class for third parties, contacts, members, users or resources.
Definition account.php:48
if( $user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition card.php:67
if(!defined('NOREQUIRESOC')) if(!defined( 'NOREQUIRETRAN')) if(!defined('NOTOKENRENEWAL')) if(!defined( 'NOREQUIREMENU')) if(!defined('NOREQUIREHTML')) if(!defined( 'NOREQUIREAJAX')) llxHeader($head='', $title='', $help_url='', $target='', $disablejs=0, $disablehead=0, $arrayofjs='', $arrayofcss='', $morequerystring='', $morecssonbody='', $replacemainareaby='', $disablenofollow=0, $disablenoindex=0)
Empty header.
Definition wrapper.php:73
Class to manage standard extra fields.
Class to build HTML component for third parties management Only common components are here.
Class to manage generation of HTML components Only common components must be here.
Class permettant la generation de composants html autre Only common components are here.
Class to manage building of HTML components.
Class of the module paid holiday.
const STATUS_APPROVED
Approved.
Class to manage projects.
Class to manage third parties objects (customers, suppliers, prospects...)
Class to manage tasks.
Class to manage Dolibarr users.
dol_time_plus_duree($time, $duration_value, $duration_unit, $ruleforendofmonth=0)
Add a delay to a date.
Definition date.lib.php:125
convertSecondToTime($iSecond, $format='all', $lengthOfDay=86400, $lengthOfWeek=7)
Return, in clear text, value of a number of seconds in days, hours and minutes.
Definition date.lib.php:244
num_public_holiday($timestampStart, $timestampEnd, $country_code='', $lastday=0, $includesaturday=-1, $includesunday=-1, $includefriday=-1, $includemonday=-1)
Return the number of non working days including Friday, Saturday and Sunday (or not) between 2 dates ...
Definition date.lib.php:769
dol_mktime($hour, $minute, $second, $month, $day, $year, $gm='auto', $check=1)
Return a timestamp date built from detailed information (by default a local PHP server timestamp) Rep...
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0, $attop=0)
Set event messages in dol_events session object.
print_liste_field_titre($name, $file="", $field="", $begin="", $param="", $moreattrib="", $sortfield="", $sortorder="", $prefix="", $tooltip="", $forcenowrapcolumntitle=0)
Show title line of an array.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2, $allowothertags=array())
Show picto whatever it's its name (generic function)
GETPOSTINT($paramname, $method=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
dol_get_fiche_head($links=array(), $active='', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limittoshow=0, $moretabssuffix='', $dragdropfile=0, $morecssdiv='')
Show tabs of a record.
dol_get_fiche_end($notab=0)
Return tab footer of a card.
natural_search($fields, $value, $mode=0, $nofirstand=0)
Generate natural SQL search string for a criteria (this criteria can be tested on one or several fiel...
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
dol_sort_array(&$array, $index, $order='asc', $natsort=0, $case_sensitive=0, $keepindex=0)
Advanced sort array by the value of a given key, which produces ascending (default) or descending out...
img_previous($titlealt='default', $moreatt='')
Show previous logo.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
img_next($titlealt='default', $moreatt='')
Show next logo.
dol_trunc($string, $size=40, $trunc='right', $stringencoding='UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding '…' if string larger than length.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
dol_getdate($timestamp, $fast=false, $forcetimezone='')
Return an array with locale date info.
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
global $conf
The following vars must be defined: $type2label $form $conf, $lang, The following vars may also be de...
Definition member.php:79
projectLinesPerDay(&$inc, $parent, $fuser, $lines, &$level, &$projectsrole, &$tasksrole, $mine, $restricteditformytask, $preselectedday, &$isavailable, $oldprojectforbreak=0, $arrayfields=array(), $extrafields=null)
Output a task line into a pertime input mode.
project_timesheet_prepare_head($mode, $fuser=null)
Prepare array with list of tabs.
restrictedArea(User $user, $features, $object=0, $tableandshare='', $feature2='', $dbt_keyfield='fk_soc', $dbt_select='rowid', $isdraft=0, $mode=0)
Check permissions of a user to show a page and an object.