dolibarr 24.0.2
time.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2005 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2006-2023 Laurent Destailleur <eldy@users.sourceforge.net>
4 * Copyright (C) 2010-2012 Regis Houssin <regis.houssin@inodbox.com>
5 * Copyright (C) 2011 Juanjo Menent <jmenent@2byte.es>
6 * Copyright (C) 2018 Ferran Marcet <fmarcet@2byte.es>
7 * Copyright (C) 2018-2025 Frédéric France <frederic.france@free.fr>
8 * Copyright (C) 2019-2021 Christophe Battarel <christophe@altairis.fr>
9 * Copyright (C) 2023 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
10 * Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
11 * Copyright (C) 2024 Vincent de Grandpré <vincent@de-grandpre.quebec>
12 * Copyright (C) 2024 Solution Libre SAS <contact@solution-libre.fr>
13 * Copyright (C) 2026 Joachim Kueter <git-jk@bloxera.com>
14 *
15 * This program is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 3 of the License, or
18 * (at your option) any later version.
19 *
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * You should have received a copy of the GNU General Public License
26 * along with this program. If not, see <https://www.gnu.org/licenses/>.
27 */
28
35// Load Dolibarr environment
36require '../../main.inc.php';
45'
46@phan-var-force ?string $uploaddir
47';
48require_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php';
49require_once DOL_DOCUMENT_ROOT . '/projet/class/task.class.php';
50require_once DOL_DOCUMENT_ROOT . '/compta/facture/class/facture.class.php';
51require_once DOL_DOCUMENT_ROOT . '/core/lib/project.lib.php';
52require_once DOL_DOCUMENT_ROOT . '/core/lib/date.lib.php';
53require_once DOL_DOCUMENT_ROOT . '/core/class/html.formother.class.php';
54require_once DOL_DOCUMENT_ROOT . '/core/class/html.formprojet.class.php';
55require_once DOL_DOCUMENT_ROOT . '/core/class/html.formintervention.class.php';
56require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
57
58// Load translation files required by the page
59$langsLoad = array('projects', 'bills', 'orders', 'companies');
60if (isModEnabled('eventorganization')) {
61 $langsLoad[] = 'eventorganization';
62}
63
64$langs->loadLangs($langsLoad);
65
66$action = GETPOST('action', 'aZ09');
67$massaction = GETPOST('massaction', 'alpha'); // The bulk action (combo box choice into lists)
68$confirm = GETPOST('confirm', 'alpha');
69$cancel = GETPOST('cancel', 'alpha');
70$toselect = GETPOST('toselect', 'array:int'); // Array of ids of elements selected into a list
71$contextpage = GETPOST('contextpage', 'aZ') ? GETPOST('contextpage', 'aZ') : 'timespentlist'; // To manage different context of search
72$backtopage = GETPOST('backtopage', 'alpha'); // Go back to a dedicated page
73$optioncss = GETPOST('optioncss', 'alpha');
74$mode = GETPOST('mode', 'alpha');
75
76$id = GETPOSTINT('id'); // Id of task
77$ref = GETPOST('ref', 'alpha'); // Ref of task
78$projectid = GETPOSTINT('projectid'); // Id of project
79$lineid = GETPOSTINT('lineid'); // Id of time spent line
80$withproject = GETPOSTINT('withproject');
81$project_ref = GETPOST('project_ref', 'alpha');
82$tab = GETPOST('tab', 'aZ09');
83
84$search_day = GETPOSTINT('search_day');
85$search_month = GETPOSTINT('search_month');
86$search_year = GETPOSTINT('search_year');
87$search_date_startday = GETPOSTINT('search_date_startday');
88$search_date_startmonth = GETPOSTINT('search_date_startmonth');
89$search_date_startyear = GETPOSTINT('search_date_startyear');
90$search_date_endday = GETPOSTINT('search_date_endday');
91$search_date_endmonth = GETPOSTINT('search_date_endmonth');
92$search_date_endyear = GETPOSTINT('search_date_endyear');
93$search_date_start = dol_mktime(0, 0, 0, $search_date_startmonth, $search_date_startday, $search_date_startyear); // Use tzserver
94$search_date_end = dol_mktime(23, 59, 59, $search_date_endmonth, $search_date_endday, $search_date_endyear);
95$search_note = GETPOST('search_note', 'alpha');
96$search_duration = GETPOST('search_duration', 'alpha');
97$search_task_ref = GETPOST('search_task_ref', 'alpha');
98$search_task_label = GETPOST('search_task_label', 'alpha');
99$search_user = GETPOST('search_user', 'intcomma');
100$search_valuebilled = GETPOST('search_valuebilled', 'intcomma');
101$search_product_ref = GETPOST('search_product_ref', 'alpha');
102$search_company = GETPOST('$search_company', 'alpha');
103$search_company_alias = GETPOST('$search_company_alias', 'alpha');
104$search_project_ref = GETPOST('$search_project_ref', 'alpha');
105$search_project_label = GETPOST('$search_project_label', 'alpha');
106$search_timespent_starthour = GETPOSTINT("search_timespent_duration_starthour");
107$search_timespent_startmin = GETPOSTINT("search_timespent_duration_startmin");
108$search_timespent_endhour = GETPOSTINT("search_timespent_duration_endhour");
109$search_timespent_endmin = GETPOSTINT("search_timespent_duration_endmin");
110
111$limit = GETPOSTINT('limit') ? GETPOSTINT('limit') : $conf->liste_limit;
112$sortfield = GETPOST('sortfield', 'aZ09comma');
113$sortorder = GETPOST('sortorder', 'aZ09comma');
114$page = GETPOSTISSET('pageplusone') ? (GETPOSTINT('pageplusone') - 1) : GETPOSTINT("page");
115if (empty($page) || $page == -1) {
116 $page = 0;
117} // If $page is not defined, or '' or -1
118$offset = $limit * $page;
119$pageprev = $page - 1;
120$pagenext = $page + 1;
121if (!$sortfield) {
122 $sortfield = 't.element_date,t.element_datehour,t.rowid';
123}
124if (!$sortorder) {
125 $sortorder = 'DESC,DESC,DESC';
126}
127
128$childids = $user->getAllChildIds(1);
129
130// Initialize a technical object to manage hooks of page. Note that conf->hooks_modules contains an array of hook context
131//$object = new TaskTime($db);
132$hookmanager->initHooks(array('projecttasktime', 'globalcard'));
133
134$object = new Task($db);
135$extrafields = new ExtraFields($db);
136$projectstatic = new Project($db);
137
138// fetch optionals attributes and labels
139$extrafields->fetch_name_optionals_label($projectstatic->table_element);
140$extrafields->fetch_name_optionals_label($object->table_element);
141
142$search_array_options = $extrafields->getOptionalsFromPost($object->table_element, '', 'search_');
143
144// Load task
145if ($id > 0 || $ref) {
146 $object->fetch($id, $ref);
147 $id = $object->id; // So when doing a search from ref, id is also set correctly.
148}
149
150
151// Security check
152$socid = 0;
153//if ($user->socid > 0) $socid = $user->socid; // For external user, no check is done on company because readability is managed by public status of project and assignment.
154if (!$user->hasRight('projet', 'lire')) {
156}
157
158if ($object->fk_project > 0) {
159 restrictedArea($user, 'projet', $object->fk_project, 'projet&project');
160} else {
161 restrictedArea($user, 'projet', 0, 'projet&project');
162 // We check user has permission to see all tasks of all users
163 if (empty($projectid) && !$user->hasRight('projet', 'all', 'lire')) {
164 $search_user = $user->id;
165 }
166}
167
168
169/*
170 * Actions
171 */
172
173$error = 0;
174
175if (GETPOST('cancel', 'alpha')) {
176 $action = '';
177}
178if (!GETPOST('confirmmassaction', 'alpha') && $massaction != 'presend' && $massaction != 'confirm_presend' && $massaction != 'confirm_generateinvoice' && $massaction != 'confirm_generateinter') {
179 $massaction = '';
180}
181
182$parameters = array('socid' => $socid, 'projectid' => $projectid);
183$reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
184if ($reshook < 0) {
185 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
186}
187
188include DOL_DOCUMENT_ROOT . '/core/actions_changeselectedfields.inc.php';
189
190// Purge search criteria
191if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter.x', 'alpha') || GETPOST('button_removefilter', 'alpha')) { // All tests are required to be compatible with all browsers
192 $search_day = '';
193 $search_month = '';
194 $search_year = '';
195 $search_note = '';
196 $search_duration = '';
197 $search_date_startday = '';
198 $search_date_startmonth = '';
199 $search_date_startyear = '';
200 $search_date_endday = '';
201 $search_date_endmonth = '';
202 $search_date_endyear = '';
203 $search_date_start = '';
204 $search_date_end = '';
205 $search_task_ref = '';
206 $search_company = '';
207 $search_company_alias = '';
208 $search_project_ref = '';
209 $search_project_label = '';
210 $search_task_label = '';
211 $search_user = -1;
212 $search_valuebilled = '';
213 $search_product_ref = '';
214 $toselect = array();
215 $search_array_options = array();
216 $search_timespent_starthour = '';
217 $search_timespent_startmin = '';
218 $search_timespent_endhour = '';
219 $search_timespent_endmin = '';
220 $action = '';
221}
222
223if ($action == 'addtimespent' && $user->hasRight('projet', 'time')) {
224 $timespent_durationhour = GETPOSTINT('timespent_durationhour');
225 $timespent_durationmin = GETPOSTINT('timespent_durationmin');
226 if (empty($timespent_durationhour) && empty($timespent_durationmin)) {
227 setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv("Duration")), null, 'errors');
228 $error++;
229 }
230 if (!GETPOSTINT("userid")) {
231 $langs->load("errors");
232 setEventMessages($langs->trans('ErrorUserNotAssignedToTask'), null, 'errors');
233 $error++;
234 }
235
236 if (!$error) {
237 if ($id || $ref) {
238 $object->fetch($id, $ref);
239 } else {
240 if (!GETPOSTINT('taskid') || GETPOSTINT('taskid') < 0) {
241 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Task")), null, 'errors');
242 $action = 'createtime';
243 $error++;
244 } else {
245 $object->fetch(GETPOSTINT('taskid'));
246 }
247 }
248
249 if (!$error) {
250 $object->fetchProject();
251
252 if (empty($object->project->status)) {
253 setEventMessages($langs->trans("ProjectMustBeValidatedFirst"), null, 'errors');
254 $action = 'createtime';
255 $error++;
256 } else {
257 $object->timespent_note = GETPOST("timespent_note", 'alpha');
258 if (GETPOSTINT('progress') > 0) {
259 $object->progress = GETPOSTINT('progress'); // If progress is -1 (not defined), we do not change value
260 }
261 $object->timespent_duration = GETPOSTINT("timespent_durationhour") * 60 * 60; // We store duration in seconds
262 $object->timespent_duration += (GETPOSTINT('timespent_durationmin') ? GETPOSTINT('timespent_durationmin') : 0) * 60; // We store duration in seconds
263 if (GETPOST("timehour") != '' && GETPOST("timehour") >= 0) { // If hour was entered
264 $object->timespent_date = dol_mktime(GETPOSTINT("timehour"), GETPOSTINT("timemin"), 0, GETPOSTINT("timemonth"), GETPOSTINT("timeday"), GETPOSTINT("timeyear"));
265 $object->timespent_withhour = 1;
266 } else {
267 $object->timespent_date = dol_mktime(12, 0, 0, GETPOSTINT("timemonth"), GETPOSTINT("timeday"), GETPOSTINT("timeyear"));
268 $object->timespent_withhour = 0;
269 }
270 $object->timespent_fk_user = GETPOSTINT("userid");
271 $object->timespent_fk_product = GETPOSTINT("fk_product");
272
273 $result = $object->addTimeSpent($user);
274
275 if ($result >= 0) {
276 setEventMessages($langs->trans("RecordSaved"), null, 'mesgs');
277 } else {
278 setEventMessages($langs->trans($object->error), null, 'errors');
279 $error++;
280 }
281 }
282 }
283 } else {
284 $action = 'createtime';
285 }
286}
287
288if (($action == 'updateline' || $action == 'updatesplitline') && !$cancel && $user->hasRight('projet', 'lire')) {
289 if (!GETPOST("new_durationhour") && !GETPOST("new_durationmin")) {
290 setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv("Duration")), null, 'errors');
291 $error++;
292 }
293
294 //If timespent date is not provided in POST (for eg, because in list the column date is hidden) we keep the actual date
295 $timespent_date = dol_mktime(12, 0, 0, GETPOSTINT("timelinemonth"), GETPOSTINT("timelineday"), GETPOSTINT("timelineyear"));
296
297 if (!$error) {
298 if ($id && GETPOSTINT('taskid') != $id) { // GETPOSTINT('taskid') is the id of new task
299 $id_temp = GETPOSTINT('taskid'); // should not overwrite $id
300
301 $object->fetchTimeSpent($lineid);
302
303 $result = 0;
304
305 $object->fetch($id_temp, $ref);
306
307 $object->timespent_note = GETPOST("timespent_note_line", "alphanohtml");
308 $object->timespent_old_duration = GETPOSTINT("old_duration");
309 $object->timespent_duration = GETPOSTINT("new_durationhour") * 60 * 60; // We store duration in seconds
310 $object->timespent_duration += (GETPOSTINT("new_durationmin") ? GETPOSTINT('new_durationmin') : 0) * 60; // We store duration in seconds
311 if (GETPOST("timelinehour") != '' && GETPOST("timelinehour") >= 0 && !empty($timespent_date)) { // If hour was entered
312 $object->timespent_date = dol_mktime(GETPOSTINT("timelinehour"), GETPOSTINT("timelinemin"), 0, GETPOSTINT("timelinemonth"), GETPOSTINT("timelineday"), GETPOSTINT("timelineyear"));
313 $object->timespent_withhour = 1;
314 } elseif (!empty($timespent_date)) {
315 $object->timespent_date = $timespent_date;
316 $object->timespent_withhour = 0;
317 }
318 // If column "by" is hidden, no userid_line is posted; keep the existing fk_user instead of resetting it to 0.
319 if (GETPOSTISSET("userid_line")) {
320 $object->timespent_fk_user = GETPOSTINT("userid_line");
321 }
322 $object->timespent_fk_product = GETPOSTINT("fk_product");
323 $object->timespent_invoiceid = GETPOSTINT("invoiceid");
324 $object->timespent_invoicelineid = GETPOSTINT("invoicelineid");
325
326 $result = 0;
327 if (in_array($object->timespent_fk_user, $childids) || $user->hasRight('projet', 'all', 'creer')) {
328 $result = $object->updateTimeSpent($user);
329 if ($result >= 0) {
330 setEventMessages($langs->trans("RecordSaved"), null, 'mesgs');
331 } else {
332 setEventMessages($langs->trans($object->error), null, 'errors');
333 $error++;
334 }
335 }
336 } else {
337 $object->fetch($id, $ref); // $object is Task
338
339 $object->fetchTimeSpent(GETPOSTINT('lineid'));
340
341 $object->timespent_note = GETPOST("timespent_note_line", "alphanohtml");
342 $object->timespent_old_duration = GETPOSTINT("old_duration");
343 $object->timespent_duration = GETPOSTINT("new_durationhour") * 60 * 60; // We store duration in seconds
344 $object->timespent_duration += (GETPOSTINT("new_durationmin") ? GETPOSTINT('new_durationmin') : 0) * 60; // We store duration in seconds
345
346 if (GETPOST("timelinehour") != '' && GETPOST("timelinehour") >= 0) { // If hour was entered
347 $object->timespent_date = dol_mktime(12, 0, 0, GETPOSTINT("timelinemonth"), GETPOSTINT("timelineday"), GETPOSTINT("timelineyear"));
348 $object->timespent_datehour = dol_mktime(GETPOSTINT("timelinehour"), GETPOSTINT("timelinemin"), 0, GETPOSTINT("timelinemonth"), GETPOSTINT("timelineday"), GETPOSTINT("timelineyear"));
349 $object->timespent_withhour = 1;
350 } elseif (!empty($timespent_date)) {
351 $object->timespent_date = $timespent_date;
352 $object->timespent_datehour = $timespent_date;
353 $object->timespent_withhour = 0;
354 }
355
356 // If column "by" is hidden, no userid_line is posted; keep the existing fk_user instead of resetting it to 0.
357 if (GETPOSTISSET("userid_line")) {
358 $object->timespent_fk_user = GETPOSTINT("userid_line");
359 }
360 $object->timespent_fk_product = GETPOSTINT("fk_product");
361 $object->timespent_invoiceid = GETPOSTINT("invoiceid");
362 $object->timespent_invoicelineid = GETPOSTINT("invoicelineid");
363
364 $result = 0;
365
366 if (in_array($object->timespent_fk_user, $childids) || $user->hasRight('projet', 'all', 'creer')) {
367 $result = $object->updateTimeSpent($user);
368
369 if ($result >= 0) {
370 setEventMessages($langs->trans("RecordSaved"), null, 'mesgs');
371 } else {
372 setEventMessages($langs->trans($object->error), null, 'errors');
373 $error++;
374 }
375 }
376 }
377 } else {
378 $action = '';
379 }
380}
381
382if ($action == 'confirm_deleteline' && $confirm == "yes" && ($user->hasRight('projet', 'time') || $user->hasRight('projet', 'all', 'creer'))) {
383 $object->fetchTimeSpent(GETPOSTINT('lineid')); // load properties like $object->timespent_xxx
384
385 if (in_array($object->timespent_fk_user, $childids) || $user->hasRight('projet', 'all', 'creer')) {
386 $result = $object->delTimeSpent($user); // delete line with $object->timespent_id
387
388 if ($result < 0) {
389 $langs->load("errors");
390 setEventMessages($langs->trans($object->error), null, 'errors');
391 $error++;
392 $action = '';
393 } else {
394 setEventMessages($langs->trans("RecordDeleted"), null, 'mesgs');
395 }
396 }
397}
398
399// Retrieve First Task ID of Project if withproject is on to allow project prev next to work
400if (!empty($project_ref) && !empty($withproject)) {
401 if ($projectstatic->fetch(0, $project_ref) > 0) {
402 $tasksarray = $object->getTasksArray(null, null, $projectstatic->id, $socid, 0);
403 if (count($tasksarray) > 0) {
404 $id = $tasksarray[0]->id;
405 } else {
406 header("Location: " . DOL_URL_ROOT . '/projet/tasks.php?id=' . $projectstatic->id . ($withproject ? '&withproject=1' : '') . (empty($mode) ? '' : '&mode=' . $mode));
407 exit;
408 }
409 }
410}
411
412// To show all time lines for project
413$projectidforalltimes = 0;
414if (GETPOSTINT('projectid') > 0) {
415 $projectidforalltimes = GETPOSTINT('projectid');
416
417 $result = $projectstatic->fetch($projectidforalltimes);
418 if (!empty($projectstatic->socid)) {
419 $projectstatic->fetch_thirdparty();
420 }
421 $res = $projectstatic->fetch_optionals();
422
423 // Quick edit for project extrafields (pencil shown on the project summary of this page)
424 if ($action == 'update_extras' && $projectstatic->id > 0) { // Test on permission already done
425 $permissiontoeditextra = $user->hasRight('projet', 'creer');
426 if (GETPOST('attribute', 'aZ09') && isset($extrafields->attributes[$projectstatic->table_element]['perms'][GETPOST('attribute', 'aZ09')])) {
427 $permissiontoeditextra = dol_eval((string) $extrafields->attributes[$projectstatic->table_element]['perms'][GETPOST('attribute', 'aZ09')]);
428 }
429
430 if ($permissiontoeditextra) {
431 $projectstatic->oldcopy = dol_clone($projectstatic, 2);
432
433 $attribute_name = GETPOST('attribute', 'aZ09');
434
435 // Fill array 'array_options' with data from update form
436 $ret = $extrafields->setOptionalsFromPost(null, $projectstatic, $attribute_name);
437 if ($ret < 0) {
438 $error++;
439 }
440
441 if (!$error) {
442 $result = $projectstatic->updateExtraField($attribute_name, 'PROJECT_MODIFY');
443 if ($result < 0) {
444 setEventMessages($projectstatic->error, $projectstatic->errors, 'errors');
445 $error++;
446 }
447 }
448 }
449
450 if ($error) {
451 $action = 'edit_extras';
452 }
453 }
454} elseif (GETPOST('project_ref', 'alpha')) {
455 $projectstatic->fetch(0, GETPOST('project_ref', 'alpha'));
456 $projectidforalltimes = $projectstatic->id;
457 $withproject = 1;
458} elseif ($id > 0) {
459 $object->fetch($id);
460 $result = $projectstatic->fetch($object->fk_project);
461}
462// If not task selected and no project selected
463$allprojectforuser = 0;
464
465if ($id <= 0 && $projectidforalltimes == 0) {
466 $allprojectforuser = $user->id;
467}
468
469if ($action == 'confirm_generateinvoice' && $user->hasRight('facture', 'creer')) {
470 if (!empty($projectstatic->socid)) {
471 $projectstatic->fetch_thirdparty();
472 }
473
474 if (!($projectstatic->thirdparty->id > 0)) {
475 setEventMessages($langs->trans("ThirdPartyRequiredToGenerateInvoice"), null, 'errors');
476 } else {
477 include_once DOL_DOCUMENT_ROOT . '/compta/facture/class/facture.class.php';
478 include_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php';
479 include_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
480
481 $tmpinvoice = new Facture($db);
482 $tmptimespent = new Task($db);
483 $tmpproduct = new Product($db);
484 $fuser = new User($db);
485 $remiseproject = price2num(GETPOST('remiseprojet', 'alphanohtml'));
486 $condidproject = GETPOSTINT('condidproject');
487
488 $db->begin();
489
490 $idprod = GETPOSTINT('productid');
491 $generateinvoicemode = GETPOST('generateinvoicemode', 'alphanohtml');
492 $invoiceToUse = GETPOSTINT('invoiceid');
493
494 $prodDurationHoursBase = 1.0;
495 $product_data_cache = array();
496 if ($idprod > 0) {
497 $result = $tmpproduct->fetch($idprod);
498 if ($result < 0) {
499 $error++;
500 setEventMessages($tmpproduct->error, $tmpproduct->errors, 'errors');
501 }
502
503 $prodDurationHoursBase = $tmpproduct->getProductDurationHours();
504 if ($prodDurationHoursBase < 0) {
505 $error++;
506 $langs->load("errors");
507 setEventMessages(null, $tmpproduct->errors, 'errors');
508 }
509
510 $dataforprice = $tmpproduct->getSellPrice($mysoc, $projectstatic->thirdparty, 0);
511
512 $pu_ht = empty($dataforprice['pu_ht']) ? 0 : $dataforprice['pu_ht'];
513 $txtva = $dataforprice['tva_tx'];
514 $localtax1 = get_localtax($txtva, 1, $projectstatic->thirdparty);
515 $localtax2 = get_localtax($txtva, 2, $projectstatic->thirdparty);
516 } else {
517 $prodDurationHoursBase = 1;
518
519 $pu_ht = 0;
520 $txtva = get_default_tva($mysoc, $projectstatic->thirdparty);
521 $localtax1 = get_default_localtax($mysoc, $projectstatic->thirdparty, 1);
522 $localtax2 = get_default_localtax($mysoc, $projectstatic->thirdparty, 2);
523 }
524
525 $tmpinvoice->socid = $projectstatic->thirdparty->id;
526 $tmpinvoice->date = dol_mktime(GETPOSTINT('rehour'), GETPOSTINT('remin'), GETPOSTINT('resec'), GETPOSTINT('remonth'), GETPOSTINT('reday'), GETPOSTINT('reyear'));
527 $tmpinvoice->fk_project = $projectstatic->id;
528 $tmpinvoice->cond_reglement_id = $condidproject;
529 $tmpinvoice->mode_reglement_id = $projectstatic->thirdparty->mode_reglement_id;
530 $tmpinvoice->fk_account = $projectstatic->thirdparty->fk_account;
531
532 if ($invoiceToUse) {
533 $tmpinvoice->fetch($invoiceToUse);
534 } else {
535 $result = $tmpinvoice->create($user);
536 if ($result <= 0) {
537 $error++;
538 setEventMessages($tmpinvoice->error, $tmpinvoice->errors, 'errors');
539 }
540 }
541
542 if (!$error) {
543 if ($generateinvoicemode == 'onelineperuser') { // 1 line per user (and per product)
544 $arrayoftasks = array();
545 foreach ($toselect as $key => $value) {
546 // Get userid, timepent
547 $object->fetchTimeSpent($value); // $value is ID of 1 line in timespent table
548 if (!array_key_exists($object->timespent_fk_user, $arrayoftasks)) {
549 $arrayoftasks[$object->timespent_fk_user] = [
550 (int) $object->timespent_fk_product => ['timespent' => 0, 'totalvaluetodivideby3600' => 0]
551 ];
552 }
553 $arrayoftasks[$object->timespent_fk_user][(int) $object->timespent_fk_product]['timespent'] += $object->timespent_duration;
554 $arrayoftasks[$object->timespent_fk_user][(int) $object->timespent_fk_product]['totalvaluetodivideby3600'] += ($object->timespent_duration * $object->timespent_thm);
555 }
556
557 foreach ($arrayoftasks as $userid => $data) { // @phan-suppress-current-line PhanEmptyForeach
558 $fuser->fetch($userid);
559 $username = $fuser->getFullName($langs);
560
561 foreach ($data as $fk_product => $timespent_data) {
562 // Define qty per hour
563 $qtyhour = $timespent_data['timespent'] / 3600;
564 $qtyhourtext = convertSecondToTime($timespent_data['timespent'], 'all', $conf->global->MAIN_DURATION_OF_WORKDAY);
565
566 // Set the unit price we want to sell the time, for this user
567 if (getDolGlobalInt('PROJECT_USE_REAL_COST_FOR_TIME_INVOICING')) {
568 // We set unit price to 0 to force the use of the rate saved during recording
569 $pu_ht = 0;
570 } elseif ($idprod <= 0) {
571 // We want to sell all the time spent with the last hourly rate of user
572 // -> but what about choice user selected ? add idprod test
573 $pu_ht = $fuser->thm;
574 }
575
576 // If no unit price known for user, we use the price recorded when recording timespent.
577 if (empty($pu_ht)) {
578 if ($timespent_data['timespent']) {
579 $pu_ht = price2num(($timespent_data['totalvaluetodivideby3600'] / $timespent_data['timespent']), 'MU');
580 }
581 }
582
583 // Add lines
584 $prodDurationHours = $prodDurationHoursBase;
585 $idprodline = $idprod;
586 $pu_htline = $pu_ht;
587 $txtvaline = $txtva;
588 $localtax1line = $localtax1;
589 $localtax2line = $localtax2;
590
591 // If a particular product/service was defined for the task
592 if (!empty($fk_product) && ($fk_product > 0) && ($fk_product !== $idprod)) {
593 if (!array_key_exists($fk_product, $product_data_cache)) {
594 $result = $tmpproduct->fetch($fk_product);
595 if ($result < 0) {
596 $error++;
597 setEventMessages($tmpproduct->error, $tmpproduct->errors, 'errors');
598 }
599 $prodDurationHours = $tmpproduct->getProductDurationHours();
600 if ($prodDurationHours < 0) {
601 $error++;
602 $langs->load("errors");
603 setEventMessages(null, $tmpproduct->errors, 'errors');
604 // Skip this entry: without it the loop reaches addline() and divides by the duration (#40805).
605 continue;
606 }
607
608 $dataforprice = $tmpproduct->getSellPrice($mysoc, $projectstatic->thirdparty, 0);
609
610 $pu_htline = empty($dataforprice['pu_ht']) ? 0 : $dataforprice['pu_ht'];
611 $txtvaline = $dataforprice['tva_tx'];
612 $localtax1line = $dataforprice['localtax1'];
613 $localtax2line = $dataforprice['localtax2'];
614
615 $product_data_cache[$fk_product] = array('duration' => $prodDurationHours, 'dataforprice' => $dataforprice);
616 } else {
617 $prodDurationHours = $product_data_cache[$fk_product]['duration'];
618 $pu_htline = empty($product_data_cache[$fk_product]['dataforprice']['pu_ht']) ? 0 : $product_data_cache[$fk_product]['dataforprice']['pu_ht'];
619 $txtvaline = $product_data_cache[$fk_product]['dataforprice']['tva_tx'];
620 $localtax1line = $product_data_cache[$fk_product]['dataforprice']['localtax1'];
621 $localtax2line = $product_data_cache[$fk_product]['dataforprice']['localtax2'];
622 }
623 $idprodline = $fk_product;
624 }
625
626 // Add lines. Pass type=1 (service) explicitly so the invoice line is tagged
627 // as a service even when no product is bound to the time entry. Otherwise
628 // the default $type=0 leaks through and the PDF labels the operation as
629 // "Delivery of goods" instead of "Provision of services" (issue #34571).
630 $lineid = $tmpinvoice->addline($langs->trans("TimeSpentForInvoice").' - '.$username . ' : ' . $qtyhourtext, $pu_htline, round($qtyhour / $prodDurationHours, 2), $txtvaline, $localtax1line, $localtax2line, ($idprodline > 0 ? $idprodline : 0), (float) $remiseproject, '', '', 0, 0, 0, 'HT', 0, Product::TYPE_SERVICE);
631 if ($lineid < 0) {
632 $error++;
633 setEventMessages(null, $tmpinvoice->errors, 'errors');
634 }
635
636 // Update lineid into line of timespent
637 $sql = 'UPDATE '.MAIN_DB_PREFIX.'element_time SET invoice_line_id = '.((int) $lineid).', invoice_id = '.((int) $tmpinvoice->id);
638 $sql .= ' WHERE rowid IN ('.$db->sanitize(implode(',', $toselect)).') AND fk_user = '.((int) $userid);
639 $result = $db->query($sql);
640 if (!$result) {
641 $error++;
642 setEventMessages($db->lasterror(), null, 'errors');
643 break;
644 }
645 }
646 }
647 } elseif ($generateinvoicemode == 'onelineperperiod') { // One line for each time spent line
648 $arrayoftasks = array();
649
650 $withdetail = GETPOST('detail_time_duration', 'alpha');
651 foreach ($toselect as $key => $value) {
652 // Get userid, timepent
653 $object->fetchTimeSpent($value);
654 // $object->id is the task id
655 $ftask = new Task($db);
656 $ftask->fetch($object->id);
657
658 $fuser->fetch($object->timespent_fk_user);
659 $username = $fuser->getFullName($langs);
660
661 $arrayoftasks[$object->timespent_id]['timespent'] = $object->timespent_duration;
662 $arrayoftasks[$object->timespent_id]['totalvaluetodivideby3600'] = $object->timespent_duration * $object->timespent_thm;
663 $arrayoftasks[$object->timespent_id]['note'] = $ftask->ref . ' - ' . $ftask->label . ' - ' . $username;
664 $arrayoftasks[$object->timespent_id]['note'] = dol_concatdesc($arrayoftasks[$object->timespent_id]['note'], $object->timespent_note);
665
666 if (!empty($withdetail)) {
667 if (!empty($object->timespent_withhour)) {
668 $arrayoftasks[$object->timespent_id]['note'] = dol_concatdesc($arrayoftasks[$object->timespent_id]['note'], $langs->trans("Date") . ': ' . dol_print_date($object->timespent_datehour));
669 } else {
670 $arrayoftasks[$object->timespent_id]['note'] = dol_concatdesc($arrayoftasks[$object->timespent_id]['note'], $langs->trans("Date") . ': ' . dol_print_date($object->timespent_date));
671 }
672 $arrayoftasks[$object->timespent_id]['note'] = dol_concatdesc($arrayoftasks[$object->timespent_id]['note'], $langs->trans("Duration") . ': ' . convertSecondToTime($object->timespent_duration, 'all', $conf->global->MAIN_DURATION_OF_WORKDAY));
673 }
674 $arrayoftasks[$object->timespent_id]['user'] = $object->timespent_fk_user;
675 $arrayoftasks[$object->timespent_id]['fk_product'] = $object->timespent_fk_product;
676 }
677
678 $pu_ht_saved = $pu_ht; // Save the base unit price (price of the selected product/service if any, 0 otherwise)
679 foreach ($arrayoftasks as $timespent_id => $value) {
680 $pu_ht = $pu_ht_saved; // Reset for each line, so a line does not inherit the unit price computed for the previous one
681 $userid = $value['user'];
682 //$pu_ht = $value['timespent'] * $fuser->thm;
683
684 // Define qty per hour
685 $qtyhour = $value['timespent'] / 3600;
686
687 // If no unit price known
688 if (empty($pu_ht)) {
689 if ($value['timespent']) {
690 $pu_ht = price2num(($value['totalvaluetodivideby3600'] / $value['timespent']), 'MU');
691 }
692 }
693
694 // Add lines
695 $prodDurationHours = $prodDurationHoursBase;
696 $idprodline = $idprod;
697 $pu_htline = $pu_ht;
698 $txtvaline = $txtva;
699 $localtax1line = $localtax1;
700 $localtax2line = $localtax2;
701
702 if (!empty($value['fk_product']) && $value['fk_product'] !== $idprod) {
703 if (!array_key_exists($value['fk_product'], $product_data_cache)) {
704 $result = $tmpproduct->fetch($value['fk_product']);
705 if ($result < 0) {
706 $error++;
707 setEventMessages($tmpproduct->error, $tmpproduct->errors, 'errors');
708 }
709 $prodDurationHours = $tmpproduct->getProductDurationHours();
710 if ($prodDurationHours < 0) {
711 $error++;
712 $langs->load("errors");
713 setEventMessages(null, $tmpproduct->errors, 'errors');
714 // Skip this entry: without it the loop reaches addline() and divides by the duration (#40805).
715 continue;
716 }
717
718 $dataforprice = $tmpproduct->getSellPrice($mysoc, $projectstatic->thirdparty, 0);
719
720 $pu_htline = empty($dataforprice['pu_ht']) ? 0 : $dataforprice['pu_ht'];
721 $txtvaline = $dataforprice['tva_tx'];
722 $localtax1line = $dataforprice['localtax1'];
723 $localtax2line = $dataforprice['localtax2'];
724
725 $product_data_cache[$value['fk_product']] = array('duration' => $prodDurationHours, 'dataforprice' => $dataforprice);
726 } else {
727 $prodDurationHours = $product_data_cache[$value['fk_product']]['duration'];
728 $pu_htline = empty($product_data_cache[$value['fk_product']]['dataforprice']['pu_ht']) ? 0 : $product_data_cache[$value['fk_product']]['dataforprice']['pu_ht'];
729 $txtvaline = $product_data_cache[$value['fk_product']]['dataforprice']['tva_tx'];
730 $localtax1line = $product_data_cache[$value['fk_product']]['dataforprice']['localtax1'];
731 $localtax2line = $product_data_cache[$value['fk_product']]['dataforprice']['localtax2'];
732 }
733 $idprodline = $value['fk_product'];
734 }
735 // Pass type=1 (service) explicitly so the invoice line is tagged as a service
736 // even when no product is bound to the time entry. Otherwise the default
737 // $type=0 leaks through and the PDF labels the operation as
738 // "Delivery of goods" instead of "Provision of services" (issue #34571).
739 $lineid = $tmpinvoice->addline($value['note'], $pu_htline, round($qtyhour / $prodDurationHours, 2), $txtvaline, $localtax1line, $localtax2line, ($idprodline > 0 ? $idprodline : 0), (float) $remiseproject, '', '', 0, 0, 0, 'HT', 0, Product::TYPE_SERVICE);
740 if ($lineid < 0) {
741 $error++;
742 setEventMessages(null, $tmpinvoice->errors, 'errors');
743 }
744 //var_dump($lineid);exit;
745
746 // Update lineid into line of timespent
747 $sql = 'UPDATE '.MAIN_DB_PREFIX.'element_time SET invoice_line_id = '.((int) $lineid).', invoice_id = '.((int) $tmpinvoice->id);
748 $sql .= ' WHERE rowid = '.((int) $timespent_id).' AND fk_user = '.((int) $userid);
749 $result = $db->query($sql);
750 if (!$result) {
751 $error++;
752 setEventMessages($db->lasterror(), null, 'errors');
753 break;
754 }
755 }
756 } elseif ($generateinvoicemode == 'onelinepertask') { // One line for each different task
757 $arrayoftasks = array();
758 foreach ($toselect as $key => $value) {
759 // Get userid, timepent
760 $object->fetchTimeSpent($value); // Call method to get list of timespent for a timespent line id (We use the utility method found into Task object)
761 // $object->id is now the task id
762 if (!isset($arrayoftasks[$object->id][(int) $object->timespent_fk_product])) {
763 $arrayoftasks[$object->id][(int) $object->timespent_fk_product] = array('timespent' => 0, 'totalvaluetodivideby3600' => 0, 'ids' => array());
764 }
765 $arrayoftasks[$object->id][(int) $object->timespent_fk_product]['timespent'] += $object->timespent_duration;
766 $arrayoftasks[$object->id][(int) $object->timespent_fk_product]['totalvaluetodivideby3600'] += ($object->timespent_duration * $object->timespent_thm);
767 // Keep the time spent lines that feed this invoice line, so the back link below
768 // points each of them at its own line and not at the last one created.
769 $arrayoftasks[$object->id][(int) $object->timespent_fk_product]['ids'][] = (int) $value;
770 }
771
772 foreach ($arrayoftasks as $task_id => $data) { // @phan-suppress-current-line PhanEmptyForeach
773 $ftask = new Task($db);
774 $ftask->fetch($task_id);
775
776 foreach ($data as $fk_product => $timespent_data) {
777 $qtyhour = $timespent_data['timespent'] / 3600;
778 $qtyhourtext = convertSecondToTime($timespent_data['timespent'], 'all', $conf->global->MAIN_DURATION_OF_WORKDAY);
779
780 // Add lines
781 $prodDurationHours = $prodDurationHoursBase;
782 $idprodline = $idprod;
783 $pu_htline = $pu_ht;
784 $txtvaline = $txtva;
785 $localtax1line = $localtax1;
786 $localtax2line = $localtax2;
787
788 if (!empty($fk_product) && $fk_product !== $idprod) {
789 if (!array_key_exists($fk_product, $product_data_cache)) {
790 $result = $tmpproduct->fetch($fk_product);
791 if ($result < 0) {
792 $error++;
793 setEventMessages($tmpproduct->error, $tmpproduct->errors, 'errors');
794 }
795 $prodDurationHours = $tmpproduct->getProductDurationHours();
796 if ($prodDurationHours < 0) {
797 $error++;
798 $langs->load("errors");
799 setEventMessages(null, $tmpproduct->errors, 'errors');
800 // Skip this entry: without it the loop reaches addline() and divides by the duration (#40805).
801 continue;
802 }
803
804 $dataforprice = $tmpproduct->getSellPrice($mysoc, $projectstatic->thirdparty, 0);
805
806 $pu_htline = empty($dataforprice['pu_ht']) ? 0 : $dataforprice['pu_ht'];
807 $txtvaline = $dataforprice['tva_tx'];
808 $localtax1line = $dataforprice['localtax1'];
809 $localtax2line = $dataforprice['localtax2'];
810
811 $product_data_cache[$fk_product] = array('duration' => $prodDurationHours, 'dataforprice' => $dataforprice);
812 } else {
813 $prodDurationHours = $product_data_cache[$fk_product]['duration'];
814 $pu_htline = empty($product_data_cache[$fk_product]['dataforprice']['pu_ht']) ? 0 : $product_data_cache[$fk_product]['dataforprice']['pu_ht'];
815 $txtvaline = $product_data_cache[$fk_product]['dataforprice']['tva_tx'];
816 $localtax1line = $product_data_cache[$fk_product]['dataforprice']['localtax1'];
817 $localtax2line = $product_data_cache[$fk_product]['dataforprice']['localtax2'];
818 }
819 $idprodline = $fk_product;
820 }
821
822
823 if ($idprodline > 0) {
824 // If a product is defined, we msut use the $prodDurationHours and $pu_ht of product (already set previously).
825 $pu_ht_for_task = $pu_htline;
826 // If we want to reuse the value of timespent (so use same price than cost price)
827 if (getDolGlobalString('PROJECT_TIME_SPENT_INTO_INVOICE_USE_VALUE')) {
828 $pu_ht_for_task = (float) price2num($timespent_data['totalvaluetodivideby3600'] / $timespent_data['timespent'], 'MU') * $prodDurationHours;
829 }
830 $pa_ht = (float) price2num($timespent_data['totalvaluetodivideby3600'] / $timespent_data['timespent'], 'MU') * $prodDurationHours;
831 } else {
832 // If not product used, we use the hour unit for duration and unit price.
833 $pu_ht_for_task = 0;
834 // If we want to reuse the value of timespent (so use same price than cost price)
835 if (getDolGlobalString('PROJECT_TIME_SPENT_INTO_INVOICE_USE_VALUE')) {
836 $pu_ht_for_task = price2num($timespent_data['totalvaluetodivideby3600'] / $timespent_data['timespent'], 'MU');
837 }
838 $pa_ht = price2num($timespent_data['totalvaluetodivideby3600'] / $timespent_data['timespent'], 'MU');
839 }
840
841 // Add lines
842 $date_start = '';
843 $date_end = '';
844 $lineName = $ftask->ref . ' - ' . $ftask->label;
845 $lineid = $tmpinvoice->addline($lineName, (float) $pu_ht_for_task, (float) price2num($qtyhour / $prodDurationHours, 'MS'), $txtvaline, $localtax1line, $localtax2line, ($idprodline > 0 ? $idprodline : 0), (float) $remiseproject, $date_start, $date_end, 0, 0, 0, 'HT', 0, 1, -1, 0, '', 0, 0, 0, $pa_ht);
846 if ($lineid < 0) {
847 $error++;
848 setEventMessages($tmpinvoice->error, $tmpinvoice->errors, 'errors');
849 break;
850 }
851
852 if (!$error) {
853 // Update lineid into line of timespent
854 $sql = 'UPDATE ' . MAIN_DB_PREFIX . 'element_time SET invoice_line_id = ' . ((int) $lineid) . ', invoice_id = ' . ((int) $tmpinvoice->id);
855 $sql .= ' WHERE rowid IN (' . $db->sanitize(implode(',', $timespent_data['ids'])) . ')';
856 $result = $db->query($sql);
857 if (!$result) {
858 $error++;
859 setEventMessages($db->lasterror(), null, 'errors');
860 break;
861 }
862 }
863 }
864 }
865 }
866 }
867
868 if (!$error) {
869 $urltoinvoice = $tmpinvoice->getNomUrl(0);
870 $mesg = $langs->trans("InvoiceGeneratedFromTimeSpent", '{s1}');
871 $mesg = str_replace('{s1}', $urltoinvoice, $mesg);
872 setEventMessages($mesg, null, 'mesgs');
873
874 $db->commit();
875 } else {
876 $db->rollback();
877 }
878 }
879}
880
881if ($action == 'confirm_generateinter' && $user->hasRight('fichinter', 'creer')) {
882 $langs->load('interventions');
883
884 if (!empty($projectstatic->socid)) {
885 $projectstatic->fetch_thirdparty();
886 }
887
888 if (!($projectstatic->thirdparty->id > 0)) {
889 setEventMessages($langs->trans("ThirdPartyRequiredToGenerateIntervention"), null, 'errors');
890 } else {
891 include_once DOL_DOCUMENT_ROOT . '/compta/facture/class/facture.class.php';
892 include_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php';
893 include_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
894
895
896 require_once DOL_DOCUMENT_ROOT . '/fichinter/class/fichinter.class.php';
897 $tmpinter = new Fichinter($db);
898 $tmptimespent = new Task($db);
899 $fuser = new User($db);
900
901 $db->begin();
902 $interToUse = GETPOSTINT('interid');
903
904
905 $tmpinter->socid = $projectstatic->thirdparty->id;
906 $tmpinter->date = dol_mktime(GETPOSTINT('rehour'), GETPOSTINT('remin'), GETPOSTINT('resec'), GETPOSTINT('remonth'), GETPOSTINT('reday'), GETPOSTINT('reyear'));
907 $tmpinter->fk_project = $projectstatic->id;
908 $tmpinter->description = $projectstatic->title . (!empty($projectstatic->description) ? '-' . $projectstatic->description : '');
909
910 if ($interToUse) {
911 $tmpinter->fetch($interToUse);
912 } else {
913 $result = $tmpinter->create($user);
914 if ($result <= 0) {
915 $error++;
916 setEventMessages($tmpinter->error, $tmpinter->errors, 'errors');
917 }
918 }
919
920 if (!$error) {
921 $arrayoftasks = array();
922 foreach ($toselect as $key => $value) {
923 // Get userid, timespent
924 $object->fetchTimeSpent($value);
925 // $object->id is the task id
926 $arrayoftasks[$object->timespent_id]['id'] = $object->id;
927 $arrayoftasks[$object->timespent_id]['timespent'] = $object->timespent_duration;
928 $arrayoftasks[$object->timespent_id]['totalvaluetodivideby3600'] = $object->timespent_duration * $object->timespent_thm;
929 $arrayoftasks[$object->timespent_id]['note'] = $object->timespent_note;
930 $arrayoftasks[$object->timespent_id]['date'] = $object->timespent_datehour;
931 }
932
933 foreach ($arrayoftasks as $timespent_id => $value) {
934 $ftask = new Task($db);
935 $ftask->fetch($value['id']);
936 // Define qty per hour
937 $qtyhour = $value['timespent'] / 3600;
938 $qtyhourtext = convertSecondToTime($value['timespent'], 'all', $conf->global->MAIN_DURATION_OF_WORKDAY);
939
940 // Add lines
941 $lineid = $tmpinter->addline($user, $tmpinter->id, $ftask->label . (!empty($value['note']) ? ' - ' . $value['note'] : ''), (int) $value['date'], $value['timespent']);
942
943 if ($lineid > 0) {
944 // Link timespent to intervention
945 $timespent = new TimeSpent($db);
946 $timespent->fetch($timespent_id);
947 $timespent->intervention_id = $tmpinter->id;
948 $timespent->intervention_line_id = $lineid;
949 $result = $timespent->update($user);
950 if ($result < 0) {
951 $error++;
952 setEventMessages($timespent->error, $timespent->errors, 'errors');
953 }
954 }
955 }
956 }
957
958 if (!$error) {
959 $urltointer = $tmpinter->getNomUrl(0);
960 $mesg = $langs->trans("InterventionGeneratedFromTimeSpent", '{s1}');
961 $mesg = str_replace('{s1}', $urltointer, $mesg);
962 setEventMessages($mesg, null, 'mesgs');
963
964 //var_dump($tmpinvoice);
965
966 $db->commit();
967 } else {
968 $db->rollback();
969 }
970 }
971}
972
973
974/*
975 * View
976 */
977
978$form = new Form($db);
979$formother = new FormOther($db);
980$formproject = new FormProjets($db);
981$userstatic = new User($db);
982//$result = $projectstatic->fetch($object->fk_project);
983$arrayofselected = is_array($toselect) ? $toselect : array();
984
985$title = $object->ref . ' - ' . $langs->trans("TimeSpent");
986if (!empty($withproject)) {
987 $title .= ' | ' . $langs->trans("Project") . (!empty($projectstatic->ref) ? ': ' . $projectstatic->ref : '');
988}
989$help_url = '';
990
991llxHeader('', $title, $help_url, '', 0, 0, '', '', '', 'mod-project project-tasks page-task_time');
992
993
994$param = ((!empty($mode) && $mode == 'mine') ? '&mode=mine' : '');
995if ($search_user) {
996 $param .= '&search_user=' . ((int) $search_user);
997}
998if ($search_month) {
999 $param .= '&search_month=' . ((int) $search_month);
1000}
1001if ($search_year) {
1002 $param .= '&search_year=' . ((int) $search_year);
1003}
1004
1005
1006if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser > 0) {
1007 // Project car in view mode
1008 if ($projectidforalltimes > 0) {
1009 $result = $projectstatic->fetch($projectidforalltimes);
1010 if (!empty($projectstatic->socid)) {
1011 $projectstatic->fetch_thirdparty();
1012 }
1013 $res = $projectstatic->fetch_optionals();
1014 } elseif ($object->fetch($id, $ref) > 0) {
1015 if (getDolGlobalString('PROJECT_ALLOW_COMMENT_ON_TASK') && empty($object->comments)) {
1016 $object->fetchComments();
1017 }
1018 $result = $projectstatic->fetch($object->fk_project);
1019 if (getDolGlobalString('PROJECT_ALLOW_COMMENT_ON_PROJECT') && method_exists($projectstatic, 'fetchComments') && empty($projectstatic->comments)) {
1020 $projectstatic->fetchComments();
1021 }
1022 if (!empty($projectstatic->socid)) {
1023 $projectstatic->fetch_thirdparty();
1024 }
1025 $res = $projectstatic->fetch_optionals();
1026
1027 $object->project = clone $projectstatic;
1028 }
1029
1030 $userRead = $projectstatic->restrictedProjectArea($user, 'read');
1031 $linktocreatetime = '';
1032
1033 if ($projectstatic->id > 0) {
1034 if ($withproject) {
1035 // Tabs for project
1036 if (empty($id) || $tab == 'timespent') {
1037 $tab = 'timespent';
1038 } else {
1039 $tab = 'tasks';
1040 }
1041
1042 $head = project_prepare_head($projectstatic);
1043 print dol_get_fiche_head($head, $tab, $langs->trans("Project"), -1, ($projectstatic->public ? 'projectpub' : 'project'));
1044
1045 // Project card
1046
1047 $linkback = '<a href="'.DOL_URL_ROOT.'/projet/list.php?restore_lastsearch_values=1">'.$langs->trans("BackToList").'</a>';
1048
1049 $morehtmlref = '<div class="refidno">';
1050 // Title
1051 $morehtmlref .= $projectstatic->title;
1052 // Thirdparty
1053 if (!empty($projectstatic->thirdparty->id) && $projectstatic->thirdparty->id > 0) {
1054 $morehtmlref .= '<br>' . $projectstatic->thirdparty->getNomUrl(1, 'project');
1055 }
1056 $morehtmlref .= '</div>';
1057
1058 // Define a complementary filter for search of next/prev ref.
1059 if (!$user->hasRight('projet', 'all', 'lire')) {
1060 $objectsListId = $projectstatic->getProjectsAuthorizedForUser($user, 0, 0);
1061 $projectstatic->next_prev_filter = "rowid:IN:" . $db->sanitize(count($objectsListId) ? implode(',', array_keys($objectsListId)) : '0');
1062 }
1063
1064 dol_banner_tab($projectstatic, 'project_ref', $linkback, 1, 'ref', 'ref', $morehtmlref, $param);
1065
1066 print '<div class="fichecenter">';
1067 print '<div class="fichehalfleft">';
1068 print '<div class="underbanner clearboth"></div>';
1069
1070 print '<table class="border tableforfield centpercent">';
1071
1072 // Usage
1073 if (getDolGlobalString('PROJECT_USE_OPPORTUNITIES') || !getDolGlobalString('PROJECT_HIDE_TASKS') || isModEnabled('eventorganization')) {
1074 print '<tr><td class="tdtop">';
1075 print $langs->trans("Usage");
1076 print '</td>';
1077 print '<td>';
1078 if (getDolGlobalString('PROJECT_USE_OPPORTUNITIES')) {
1079 print '<input type="checkbox" disabled name="usage_opportunity"' . (GETPOSTISSET('usage_opportunity') ? (GETPOST('usage_opportunity', 'alpha') != '' ? ' checked="checked"' : '') : ($projectstatic->usage_opportunity ? ' checked="checked"' : '')) . '"> ';
1080 $htmltext = $langs->trans("ProjectFollowOpportunity");
1081 print $form->textwithpicto($langs->trans("ProjectFollowOpportunity"), $htmltext);
1082 print '<br>';
1083 }
1084 if (!getDolGlobalString('PROJECT_HIDE_TASKS')) {
1085 print '<input type="checkbox" disabled name="usage_task"' . (GETPOSTISSET('usage_task') ? (GETPOST('usage_task', 'alpha') != '' ? ' checked="checked"' : '') : ($projectstatic->usage_task ? ' checked="checked"' : '')) . '"> ';
1086 $htmltext = $langs->trans("ProjectFollowTasks");
1087 print $form->textwithpicto($langs->trans("ProjectFollowTasks"), $htmltext);
1088 print '<br>';
1089 }
1090 if (!getDolGlobalString('PROJECT_HIDE_TASKS') && getDolGlobalString('PROJECT_BILL_TIME_SPENT')) {
1091 print '<input type="checkbox" disabled name="usage_bill_time"' . (GETPOSTISSET('usage_bill_time') ? (GETPOST('usage_bill_time', 'alpha') != '' ? ' checked="checked"' : '') : ($projectstatic->usage_bill_time ? ' checked="checked"' : '')) . '"> ';
1092 $htmltext = $langs->trans("ProjectBillTimeDescription");
1093 print $form->textwithpicto($langs->trans("BillTime"), $htmltext);
1094 print '<br>';
1095 }
1096 if (isModEnabled('eventorganization')) {
1097 print '<input type="checkbox" disabled name="usage_organize_event"' . (GETPOSTISSET('usage_organize_event') ? (GETPOST('usage_organize_event', 'alpha') != '' ? ' checked="checked"' : '') : ($projectstatic->usage_organize_event ? ' checked="checked"' : '')) . '"> ';
1098 $htmltext = $langs->trans("EventOrganizationDescriptionLong");
1099 print $form->textwithpicto($langs->trans("ManageOrganizeEvent"), $htmltext);
1100 }
1101 print '</td></tr>';
1102 }
1103
1104 // Budget
1105 print '<tr><td>' . $langs->trans("Budget") . '</td><td>';
1106 if (!is_null($projectstatic->budget_amount) && strcmp($projectstatic->budget_amount, '')) {
1107 print '<span class="amount">' . price($projectstatic->budget_amount, 0, $langs, 1, 0, 0, $conf->currency) . '</span>';
1108 }
1109 print '</td></tr>';
1110
1111 // Date start - end project
1112 print '<tr><td>' . $langs->trans("Dates") . '</td><td>';
1113 $start = dol_print_date($projectstatic->date_start, 'day');
1114 print($start ? $start : '?');
1115 $end = dol_print_date($projectstatic->date_end, 'day');
1116 print ' - ';
1117 print($end ? $end : '?');
1118 if ($projectstatic->hasDelay()) {
1119 print img_warning("Late");
1120 }
1121 print '</td></tr>';
1122
1123 // Visibility
1124 print '<tr><td class="titlefield">' . $langs->trans("Visibility") . '</td><td>';
1125 if ($projectstatic->public) {
1126 print img_picto($langs->trans('SharedProject'), 'world', 'class="paddingrightonly"');
1127 print $langs->trans('SharedProject');
1128 } else {
1129 print img_picto($langs->trans('PrivateProject'), 'private', 'class="paddingrightonly"');
1130 print $langs->trans('PrivateProject');
1131 }
1132 print '</td></tr>';
1133
1134 // Other attributes
1135 $cols = 2;
1136 $savobject = $object;
1137 $object = $projectstatic;
1138 // On this page, the 'id' GET parameter is the task id, not the project id, so force the
1139 // edit link/form of the project extrafields to use 'projectid' instead, and keep 'withproject'
1140 // so the project summary (this whole block) stays visible after the edit link is followed.
1141 $forcefieldid = 'projectid';
1142 $moreparam = '&withproject=1';
1143 include DOL_DOCUMENT_ROOT . '/core/tpl/extrafields_view.tpl.php';
1144 unset($forcefieldid, $moreparam);
1145 $object = $savobject;
1146
1147 print '</table>';
1148
1149 print '</div>';
1150 print '<div class="fichehalfright">';
1151 print '<div class="underbanner clearboth"></div>';
1152
1153 print '<table class="border tableforfield centpercent">';
1154
1155 // Categories
1156 if (isModEnabled('category')) {
1157 print '<tr><td class="valignmiddle">' . $langs->trans("Categories") . '</td><td>';
1158 print $form->showCategories($projectstatic->id, 'project', 1);
1159 print "</td></tr>";
1160 }
1161
1162 // Description
1163 print '<tr><td class="titlefield'.($projectstatic->description ? ' noborderbottom' : '').'" colspan="2">'.$langs->trans("Description").'</td></tr>';
1164 if ($projectstatic->description) {
1165 print '<tr><td class="nottitleforfield" colspan="2">';
1166 print '<div class="longmessagecut">';
1167 print dolPrintHTML($projectstatic->description);
1168 print '</div>';
1169 print '</td></tr>';
1170 }
1171
1172 print '</table>';
1173
1174 print '</div>';
1175 print '</div>';
1176
1177 print '<div class="clearboth"></div>';
1178
1179 print dol_get_fiche_end();
1180
1181 print '<br>';
1182 }
1183
1184 // Link to create time
1185 $linktocreatetimeBtnStatus = 0;
1186 $linktocreatetimeUrl = '';
1187 $linktocreatetimeHelpText = '';
1188 if ($user->hasRight('projet', 'time')) {
1189 if ($projectstatic->public || $userRead > 0) {
1190 $linktocreatetimeBtnStatus = 1;
1191
1192 if (!empty($projectidforalltimes)) {
1193 // We are on tab 'Time Spent' of project
1194 $backtourl = $_SERVER['PHP_SELF'] . '?projectid=' . $projectstatic->id . ($withproject ? '&withproject=1' : '');
1195 $linktocreatetimeUrl = $_SERVER['PHP_SELF'] . '?' . ($withproject ? 'withproject=1' : '') . '&projectid=' . $projectstatic->id . '&action=createtime&token=' . newToken() . $param . '&backtopage=' . urlencode($backtourl);
1196 } else {
1197 // We are on tab 'Time Spent' of task
1198 $backtourl = $_SERVER['PHP_SELF'] . '?id=' . $object->id . ($withproject ? '&withproject=1' : '');
1199 $linktocreatetimeUrl = $_SERVER['PHP_SELF'] . '?' . ($withproject ? 'withproject=1' : '') . ($object->id > 0 ? '&id=' . $object->id : '&projectid=' . $projectstatic->id) . '&action=createtime&token=' . newToken() . $param . '&backtopage=' . urlencode($backtourl);
1200 }
1201 } else {
1202 $linktocreatetimeBtnStatus = -2;
1203 $linktocreatetimeHelpText = $langs->trans("NotOwnerOfProject");
1204 }
1205 } else {
1206 $linktocreatetimeBtnStatus = -2;
1207 $linktocreatetimeHelpText = $langs->trans("NotEnoughPermissions");
1208 }
1209
1210 $paramsbutton = array('morecss' => 'reposition');
1211 $linktocreatetime = dolGetButtonTitle($langs->trans('AddTimeSpent'), $linktocreatetimeHelpText, 'fa fa-plus-circle', $linktocreatetimeUrl, '', $linktocreatetimeBtnStatus, $paramsbutton);
1212 }
1213
1214 $massactionbutton = '';
1215 $arrayofmassactions = array();
1216
1217 if ($projectstatic->id > 0) {
1218 // If we are on a given project.
1219 if ($projectstatic->usage_bill_time) {
1220 $arrayofmassactions = array(
1221 'generateinvoice' => $langs->trans("GenerateBill"),
1222 //'builddoc'=>$langs->trans("PDFMerge"),
1223 );
1224 }
1225 if (isModEnabled('intervention') && $user->hasRight('ficheinter', 'creer')) {
1226 $langs->load("interventions");
1227 $arrayofmassactions['generateinter'] = $langs->trans("GenerateInter");
1228 }
1229 }
1230 if (in_array($massaction, array('presend', 'predelete', 'generateinvoice', 'generateinter'))) {
1231 $arrayofmassactions = array();
1232 }
1233 $massactionbutton = $form->selectMassAction('', $arrayofmassactions);
1234
1235 $formconfirm = '';
1236
1237 // Task
1238
1239 // Show section with information of task. If id of task is not defined and project id defined, then $projectidforalltimes is not empty.
1240 if (empty($projectidforalltimes) && empty($allprojectforuser)) {
1241 $head = task_prepare_head($object);
1242 print dol_get_fiche_head($head, 'task_time', $langs->trans("Task"), -1, 'projecttask', 0, '', 'reposition');
1243
1244 $param = (GETPOST('withproject') ? '&withproject=1' : '');
1245 $linkback = GETPOST('withproject') ? '<a href="'.DOL_URL_ROOT.'/projet/tasks.php?id='.$projectstatic->id.'">'.$langs->trans("BackToList").'</a>' : '';
1246
1247 if ($action == 'deleteline') {
1248 $urlafterconfirm = $_SERVER["PHP_SELF"] . "?" . ($object->id > 0 ? "id=" . $object->id : 'projectid=' . $projectstatic->id) . '&lineid=' . GETPOSTINT("lineid") . $param;
1249 $formconfirm .= $form->formconfirm($urlafterconfirm, $langs->trans("DeleteATimeSpent"), $langs->trans("ConfirmDeleteATimeSpent"), "confirm_deleteline", '', '', 1);
1250 }
1251
1252 if (!GETPOST('withproject') || empty($projectstatic->id)) {
1253 $projectsListId = $projectstatic->getProjectsAuthorizedForUser($user, 0, 1);
1254 $object->next_prev_filter = "fk_projet:IN:" . $db->sanitize($projectsListId);
1255 } else {
1256 $object->next_prev_filter = "fk_projet:=:" . ((int) $projectstatic->id);
1257 }
1258
1259 $morehtmlref = '';
1260
1261 // Project
1262 if (empty($withproject)) {
1263 $morehtmlref .= '<div class="refidno">';
1264 $morehtmlref .= $langs->trans("Project") . ': ';
1265 $morehtmlref .= $projectstatic->getNomUrl(1);
1266 $morehtmlref .= '<br>';
1267
1268 // Third party
1269 $morehtmlref .= $langs->trans("ThirdParty") . ': ';
1270 if (!empty($projectstatic->thirdparty) && is_object($projectstatic->thirdparty)) {
1271 $morehtmlref .= $projectstatic->thirdparty->getNomUrl(1);
1272 }
1273 $morehtmlref .= '</div>';
1274 }
1275
1276 dol_banner_tab($object, 'ref', $linkback, 1, 'ref', 'ref', $morehtmlref, $param);
1277
1278 print '<div class="fichecenter">';
1279 print '<div class="fichehalfleft">';
1280
1281 print '<div class="underbanner clearboth"></div>';
1282 print '<table class="border centpercent tableforfield">';
1283
1284 // Task parent
1285 print '<tr><td>' . $langs->trans("ParentTask") . '</td><td>';
1286 if ($object->fk_task_parent > 0) {
1287 $tasktmp = new Task($db);
1288 $tasktmp->fetch($object->fk_task_parent);
1289 print $tasktmp->getNomUrl(1);
1290 }
1291 print '</td></tr>';
1292
1293 // Date start - Date end task
1294 print '<tr><td class="titlefield">' . $langs->trans("DateStart") . ' - ' . $langs->trans("Deadline") . '</td><td>';
1295 $start = dol_print_date($object->date_start, 'dayhour');
1296 print($start ? $start : '?');
1297 $end = dol_print_date($object->date_end, 'dayhour');
1298 print ' - ';
1299 print($end ? $end : '?');
1300 if ($object->hasDelay()) {
1301 print img_warning("Late");
1302 }
1303 print '</td></tr>';
1304
1305 // Planned workload
1306 print '<tr><td>' . $langs->trans("PlannedWorkload") . '</td><td>';
1307 if ($object->planned_workload) {
1308 print convertSecondToTime($object->planned_workload, 'allhourmin');
1309 }
1310 print '</td></tr>';
1311
1312 print '</table>';
1313 print '</div>';
1314
1315 print '<div class="fichehalfright">';
1316
1317 print '<div class="underbanner clearboth"></div>';
1318 print '<table class="border tableforfield centpercent">';
1319
1320 // Progress declared
1321 print '<tr><td class="titlefield">' . $langs->trans("ProgressDeclared") . '</td><td>';
1322 print $object->progress != '' ? $object->progress . ' %' : '';
1323 print '</td></tr>';
1324
1325 // Progress calculated
1326 print '<tr><td>' . $langs->trans("ProgressCalculated") . '</td><td>';
1327 if ($object->planned_workload) {
1328 $tmparray = $object->getSummaryOfTimeSpent();
1329 if ($tmparray['total_duration'] > 0) {
1330 print round($tmparray['total_duration'] / $object->planned_workload * 100, 2) . ' %';
1331 } else {
1332 print '0 %';
1333 }
1334 } else {
1335 print '<span class="opacitymedium">' . $langs->trans("WorkloadNotDefined") . '</span>';
1336 }
1337 print '</td>';
1338
1339 print '</tr>';
1340
1341 print '</table>';
1342
1343 print '</div>';
1344
1345 print '</div>';
1346 print '<div class="clearboth"></div>';
1347
1348 print dol_get_fiche_end();
1349 } else {
1350 if ($action == 'deleteline') {
1351 $urlafterconfirm = $_SERVER["PHP_SELF"] . "?" . ($projectstatic->id > 0 ? 'projectid=' . $projectstatic->id : ($object->id > 0 ? "id=" . $object->id : '')) . '&lineid=' . GETPOSTINT("lineid") . ($withproject ? '&withproject=1' : ''). "&contextpage=" . urlencode($contextpage).$param;
1352 $formconfirm .= $form->formconfirm($urlafterconfirm, $langs->trans("DeleteATimeSpent"), $langs->trans("ConfirmDeleteATimeSpent"), "confirm_deleteline", '', '', 1);
1353 }
1354 }
1355
1356 // Call Hook formConfirm
1357 $parameters = array('formConfirm' => $formconfirm, "projectstatic" => $projectstatic, "withproject" => $withproject);
1358 $reshook = $hookmanager->executeHooks('formConfirm', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
1359 if (empty($reshook)) {
1360 $formconfirm .= $hookmanager->resPrint;
1361 } elseif ($reshook > 0) {
1362 $formconfirm = $hookmanager->resPrint;
1363 }
1364
1365
1366 if ($projectstatic->id > 0 || $allprojectforuser > 0) {
1367 // Initialize a technical object to manage hooks. Note that conf->hooks_modules contains array
1368 $hookmanager->initHooks(array('tasktimelist'));
1369
1370 // Print form confirm
1371 print $formconfirm;
1372
1373 // Definition of fields for list
1374 $arrayfields = array();
1375 $arrayfields['t.element_date'] = array('label' => $langs->trans("Date"), 'checked' => '1');
1376 $arrayfields['p.fk_soc'] = array('label' => $langs->trans("ThirdParty"), 'type' => 'integer:Societe:/societe/class/societe.class.php:1','checked' => '1');
1377 $arrayfields['s.name_alias'] = array('label' => $langs->trans("AliasNameShort"), 'type' => 'integer:Societe:/societe/class/societe.class.php:1');
1378 if ((empty($id) && empty($ref)) || !empty($projectidforalltimes)) { // Not a dedicated task
1379 if (! empty($allprojectforuser)) {
1380 $arrayfields['p.project_ref'] = ['label' => $langs->trans('RefProject'), 'checked' => '1'];
1381 $arrayfields['p.project_label'] = ['label' => $langs->trans('ProjectLabel'), 'checked' => '1'];
1382 }
1383 $arrayfields['t.element_ref'] = array('label' => $langs->trans("RefTask"), 'checked' => '1');
1384 $arrayfields['t.element_label'] = array('label' => $langs->trans("LabelTask"), 'checked' => '1');
1385 }
1386 $arrayfields['author'] = array('label' => $langs->trans("By"), 'checked' => '1');
1387 $arrayfields['t.note'] = array('label' => $langs->trans("Note"), 'checked' => '1');
1388 $arrayfields['t.tms'] = array('label' => $langs->trans("DateModification"), 'checked' => '0');
1389 if (isModEnabled('service') && !empty($projectstatic->thirdparty) && $projectstatic->thirdparty->id > 0 && $projectstatic->usage_bill_time) {
1390 $arrayfields['t.fk_product'] = array('label' => $langs->trans("Product"), 'checked' => '1');
1391 }
1392 $arrayfields['t.element_duration'] = array('label' => $langs->trans("Duration"), 'checked' => '1');
1393 $arrayfields['t.fk_product'] = array('label' => $langs->trans("Product"), 'checked' => '0', 'enabled' => (string) (int) (((getDolGlobalInt('PROJECT_HIDE_TASKS') || !getDolGlobalInt('PROJECT_BILL_TIME_SPENT')) ? 0 : 1) && $projectstatic->usage_bill_time));
1394 $arrayfields['value'] = array('label' => $langs->trans("Value"), 'checked' => '1', 'enabled' => (string) (int) isModEnabled("salaries"));
1395 $arrayfields['valuebilled'] = array('label' => $langs->trans("Billed"), 'checked' => '1', 'enabled' => (string) (int) (((getDolGlobalInt('PROJECT_HIDE_TASKS') || !getDolGlobalInt('PROJECT_BILL_TIME_SPENT')) ? 0 : 1) && $projectstatic->usage_bill_time));
1396 // Extra fields
1397 include DOL_DOCUMENT_ROOT . '/core/tpl/extrafields_list_array_fields.tpl.php';
1398
1399 $arrayfields = dol_sort_array($arrayfields, 'position');
1400
1401 $param = '';
1402 if (!empty($contextpage) && $contextpage != $_SERVER["PHP_SELF"]) {
1403 $param .= '&contextpage=' . urlencode($contextpage);
1404 }
1405 if ($limit > 0 && $limit != $conf->liste_limit) {
1406 $param .= '&limit='.((int) $limit);
1407 }
1408 if ($id > 0) {
1409 $param .= '&id='.((int) $id);
1410 }
1411 if ($search_month > 0) {
1412 $param .= '&search_month=' . urlencode((string) ($search_month));
1413 }
1414 if ($search_year > 0) {
1415 $param .= '&search_year=' . urlencode((string) ($search_year));
1416 }
1417 if (!empty($search_user)) { // We keep param if -1 because default value is forced to user id if not set
1418 $param .= '&search_user='.urlencode($search_user);
1419 }
1420 if ($search_task_ref != '') {
1421 $param .= '&search_task_ref=' . urlencode($search_task_ref);
1422 }
1423 if ($search_company != '') {
1424 $param .= '&search_company=' . urlencode($search_company);
1425 }
1426 if ($search_company_alias != '') {
1427 $param .= '&search_company_alias=' . urlencode($search_company_alias);
1428 }
1429 if ($search_project_ref != '') {
1430 $param .= '&search_project_ref=' . urlencode($search_project_ref);
1431 }
1432 if ($search_project_label != '') {
1433 $param .= '&search_project_label=' . urlencode($search_project_label);
1434 }
1435 if ($search_task_label != '') {
1436 $param .= '&search_task_label=' . urlencode($search_task_label);
1437 }
1438 if ($search_note != '') {
1439 $param .= '&search_note=' . urlencode($search_note);
1440 }
1441 if ($search_duration != '') {
1442 $param .= '&search_field2=' . urlencode((string) ($search_duration));
1443 }
1444 if ($optioncss != '') {
1445 $param .= '&optioncss=' . urlencode($optioncss);
1446 }
1447 if ($search_date_startday) {
1448 $param .= '&search_date_startday=' . urlencode((string) ($search_date_startday));
1449 }
1450 if ($search_date_startmonth) {
1451 $param .= '&search_date_startmonth=' . urlencode((string) ($search_date_startmonth));
1452 }
1453 if ($search_date_startyear) {
1454 $param .= '&search_date_startyear=' . urlencode((string) ($search_date_startyear));
1455 }
1456 if ($search_date_endday) {
1457 $param .= '&search_date_endday=' . urlencode((string) ($search_date_endday));
1458 }
1459 if ($search_date_endmonth) {
1460 $param .= '&search_date_endmonth=' . urlencode((string) ($search_date_endmonth));
1461 }
1462 if ($search_date_endyear) {
1463 $param .= '&search_date_endyear=' . urlencode((string) ($search_date_endyear));
1464 }
1465 if ($search_timespent_starthour) {
1466 $param .= '&search_timespent_duration_starthour=' . urlencode((string) ($search_timespent_starthour));
1467 }
1468 if ($search_timespent_startmin) {
1469 $param .= '&search_timespent_duration_startmin=' . urlencode((string) ($search_timespent_startmin));
1470 }
1471 if ($search_timespent_endhour) {
1472 $param .= '&search_timespent_duration_endhour=' . urlencode((string) ($search_timespent_endhour));
1473 }
1474 if ($search_timespent_endmin) {
1475 $param .= '&search_timespent_duration_endmin=' . urlencode((string) ($search_timespent_endmin));
1476 }
1477 // Add $param from extra fields
1478 include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_param.tpl.php';
1479 if ($id) {
1480 $param .= '&id=' . urlencode((string) ($id));
1481 }
1482 if ($projectid) {
1483 $param .= '&projectid=' . urlencode((string) ($projectid));
1484 }
1485 if ($withproject) {
1486 $param .= '&withproject=' . urlencode((string) ($withproject));
1487 }
1488 // Add $param from hooks
1489 $parameters = array('param' => &$param);
1490 $reshook = $hookmanager->executeHooks('printFieldListSearchParam', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
1491 $param .= $hookmanager->resPrint;
1492
1493 print '<form method="POST" action="' . $_SERVER["PHP_SELF"] . '">';
1494 if ($optioncss != '') {
1495 print '<input type="hidden" name="optioncss" value="' . $optioncss . '">';
1496 }
1497 print '<input type="hidden" name="token" value="' . newToken() . '">';
1498 print '<input type="hidden" name="formfilteraction" id="formfilteraction" value="list">';
1499 if ($action == 'editline') {
1500 print '<input type="hidden" name="action" value="updateline">';
1501 } elseif ($action == 'splitline') {
1502 print '<input type="hidden" name="action" value="updatesplitline">';
1503 } elseif ($action == 'createtime' && $user->hasRight('projet', 'time')) {
1504 print '<input type="hidden" name="action" value="addtimespent">';
1505 } elseif ($massaction == 'generateinvoice' && $user->hasRight('facture', 'creer')) {
1506 print '<input type="hidden" name="action" value="confirm_generateinvoice">';
1507 } elseif ($massaction == 'generateinter' && $user->hasRight('ficheinter', 'creer')) {
1508 print '<input type="hidden" name="action" value="confirm_generateinter">';
1509 } else {
1510 print '<input type="hidden" name="action" value="list">';
1511 }
1512 print '<input type="hidden" name="sortfield" value="' . $sortfield . '">';
1513 print '<input type="hidden" name="sortorder" value="' . $sortorder . '">';
1514
1515 // Carry the task id through the filter submit so that the timespent list
1516 // stays scoped to the current task instead of falling back to the global view.
1517 print '<input type="hidden" name="id" value="' . ((int) $id) . '">';
1518 print '<input type="hidden" name="projectid" value="' . $projectidforalltimes . '">';
1519 print '<input type="hidden" name="withproject" value="' . $withproject . '">';
1520 print '<input type="hidden" name="tab" value="' . $tab . '">';
1521 print '<input type="hidden" name="page_y" value="">';
1522
1523 // Form to convert time spent into invoice
1524 if ($massaction == 'generateinvoice') {
1525 if (!empty($projectstatic->thirdparty) && $projectstatic->thirdparty->id > 0) {
1526 print '<table class="noborder centerpercent">';
1527 print '<tr>';
1528 print '<td class="titlefield">';
1529 print $langs->trans('DateInvoice');
1530 print '</td>';
1531 print '<td>';
1532 print $form->selectDate('', '', 0, 0, 0, '', 1, 1);
1533 print '</td>';
1534 print '</tr>';
1535
1536 print '<tr>';
1537 print '<td>';
1538 print $langs->trans('Mode');
1539 print '</td>';
1540 print '<td>';
1541 $tmparray = array(
1542 'onelineperuser' => 'OneLinePerUser',
1543 'onelinepertask' => 'OneLinePerTask',
1544 'onelineperperiod' => 'OneLinePerTimeSpentLine',
1545 );
1546 print $form->selectarray('generateinvoicemode', $tmparray, 'onelineperuser', 0, 0, 0, '', 1);
1547 print "\n" . '<script type="text/javascript">';
1548 print '
1549 $(document).ready(function () {
1550 setDetailVisibility();
1551 $("#generateinvoicemode").change(function() {
1552 setDetailVisibility();
1553 });
1554 function setDetailVisibility() {
1555 generateinvoicemode = $("#generateinvoicemode option:selected").val();
1556 if (generateinvoicemode=="onelineperperiod") {
1557 $("#detail_time_duration").show();
1558 } else {
1559 $("#detail_time_duration").hide();
1560 }
1561 }
1562 });
1563 ';
1564 print '</script>' . "\n";
1565 print '<span style="display:none" id="detail_time_duration"><input type="checkbox" value="detail" name="detail_time_duration"/>' . $langs->trans('AddDetailDateAndDuration') . '</span>';
1566 print '</td>';
1567 print '</tr>';
1568
1569 if (isModEnabled("service")) {
1570 print '<tr>';
1571 print '<td>';
1572 print $langs->trans('ServiceToUseOnLines');
1573 print '</td>';
1574 print '<td>';
1575 $form->select_produits(0, 'productid', 1, 0, $projectstatic->thirdparty->price_level, 1, 2, '', 0, array(), $projectstatic->thirdparty->id, 'None', 0, 'maxwidth500');
1576 print '</td>';
1577 print '</tr>';
1578 }
1579 print '<tr>';
1580 print '<td class="titlefield">';
1581 print $langs->trans('InvoiceToUse');
1582 print '</td>';
1583 print '<td>';
1584 print $form->selectInvoiceForTimeProject($projectstatic->thirdparty->id, '', 'invoiceid', 24, 0, $langs->trans('NewInvoice'), 1, 0, 0, 'maxwidth500', '', 'all');
1585 print '</td>';
1586 print '</tr>';
1587 print '<tr>';
1588 print '<td class="titlefield">';
1589 print $langs->trans("CustomerRelativeDiscount");
1590 print '</td>';
1591 print '<td>';
1592 print '<input type="text" size="5" name="remiseproject" value="'.$projectstatic->thirdparty->remise_percent.'">%';
1593 print '</td>';
1594 print '</tr>';
1595 print '<tr class="newinvoicedetail">';
1596 print '<td class="titlefield">';
1597 print $langs->trans("PaymentConditions");
1598 print '</td>';
1599 print '<td>';
1600 print $form->getSelectConditionsPaiements((int) $projectstatic->thirdparty->cond_reglement_id, 'condidproject');
1601 print '</td>';
1602 print '</tr>';
1603 /*print '<tr>';
1604 print '<td>';
1605 print $langs->trans('ValidateInvoices');
1606 print '</td>';
1607 print '<td>';
1608 print $form->selectyesno('validate_invoices', 0, 1);
1609 print '</td>';
1610 print '</tr>';*/
1611 print '</table>';
1612
1613 print '<br>';
1614 print '<div class="center">';
1615 print '<input type="submit" class="button" id="createbills" name="createbills" value="' . $langs->trans('GenerateBill') . '"> ';
1616 print '<input type="submit" class="button button-cancel" id="cancel" name="cancel" value="' . $langs->trans("Cancel") . '">';
1617 print '</div>';
1618 print '<br>';
1619 } else {
1620 print '<div class="warning">' . $langs->trans("ThirdPartyRequiredToGenerateInvoice") . '</div>';
1621 print '<div class="center">';
1622 print '<input type="submit" class="button button-cancel" id="cancel" name="cancel" value="' . $langs->trans("Cancel") . '">';
1623 print '</div>';
1624 $massaction = '';
1625 }
1626 } elseif ($massaction == 'generateinter') {
1627 // Form to convert time spent into invoice
1628 print '<input type="hidden" name="massaction" value="confirm_createinter">';
1629
1630 if (!empty($projectstatic->thirdparty) && $projectstatic->thirdparty->id > 0) {
1631 print '<br>';
1632 print '<table class="noborder centpercent">';
1633 print '<tr>';
1634 print '<td class="titlefield">';
1635 print img_picto('', 'intervention', 'class="pictofixedwidth"') . $langs->trans('InterToUse');
1636 print '</td>';
1637 print '<td>';
1638 $forminter = new FormIntervention($db);
1639 print $forminter->select_interventions($projectstatic->thirdparty->id, 0, 'interid', 24, $langs->trans('NewInter'), true);
1640 print '</td>';
1641 print '</tr>';
1642 print '</table>';
1643
1644 print '<div class="center">';
1645 print '<input type="submit" class="button" id="createinter" name="createinter" value="' . $langs->trans('GenerateInter') . '"> ';
1646 print '<input type="submit" class="button" id="cancel" name="cancel" value="' . $langs->trans('Cancel') . '">';
1647 print '</div>';
1648 print '<br>';
1649 } else {
1650 print '<div class="warning">' . $langs->trans("ThirdPartyRequiredToGenerateIntervention") . '</div>';
1651 print '<div class="center">';
1652 print '<input type="submit" class="button" id="cancel" name="cancel" value="' . $langs->trans('Cancel') . '">';
1653 print '</div>';
1654 $massaction = '';
1655 }
1656 }
1657
1658 // Allow Pre-Mass-Action hook (eg for confirmation dialog)
1659 $parameters = array(
1660 'toselect' => $toselect,
1661 'uploaddir' => isset($uploaddir) ? $uploaddir : null
1662 );
1663
1664 $reshook = $hookmanager->executeHooks('doPreMassActions', $parameters, $object, $action);
1665 if ($reshook < 0) {
1666 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
1667 } else {
1668 print $hookmanager->resPrint;
1669 }
1670
1671 /*
1672 * List of time spent
1673 */
1674 $tasks = array();
1675
1676 $varpage = empty($contextpage) ? $_SERVER["PHP_SELF"] : $contextpage;
1677 $selectedfields = $form->multiSelectArrayWithCheckbox('selectedfields', $arrayfields, $varpage, $conf->main_checkbox_left_column); // This also change content of $arrayfields
1678
1679 // If project has usage "Bill time", we force visibility of field "billed"
1680 if ($projectstatic->usage_bill_time) {
1681 $arrayfields['valuebilled']['checked'] = 1;
1682 }
1683
1684 $sql = "SELECT t.rowid, t.fk_element, t.element_date, t.element_datehour, t.element_date_withhour, t.element_duration, t.fk_user, t.note, t.thm,";
1685 $sql .= " t.fk_product, t.invoice_line_id, t.import_key, t.datec, t.tms,";
1686 $sql .= " pt.rowid as taskid, pt.ref, pt.label, pt.fk_projet, pt.billable,";
1687 $sql .= " u.lastname, u.firstname, u.login, u.photo, u.gender, u.statut as user_status,";
1688 $sql .= " il.fk_facture as invoice_id, inv.fk_statut,";
1689 $sql .= " p.fk_soc, s.name_alias";
1690 if (!empty($extrafields->attributes['projet_task']['label'])) {
1691 foreach ($extrafields->attributes['projet_task']['label'] as $key => $val) {
1692 $sql .= ($extrafields->attributes['projet_task']['type'][$key] != 'separate' ? ",efpt.".$key." as options_".$key : '');
1693 }
1694 }
1695 // Add fields from hooks
1696 $parameters = array();
1697 $reshook = $hookmanager->executeHooks('printFieldListSelect', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
1698 $sql .= $hookmanager->resPrint;
1699 $sql = preg_replace('/,\s*$/', '', $sql);
1700
1701 $sqlfields = $sql; // $sql fields to remove for count total
1702
1703 $sql .= " FROM ".MAIN_DB_PREFIX."element_time as t";
1704 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."facturedet as il ON il.rowid = t.invoice_line_id";
1705 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."facture as inv ON inv.rowid = il.fk_facture";
1706 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as prod ON prod.rowid = t.fk_product";
1707 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."projet_task as pt ON pt.rowid = t.fk_element";
1708 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task_extrafields as efpt ON pt.rowid = efpt.fk_object";
1709 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."projet as p ON p.rowid = pt.fk_projet";
1710 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."user as u ON t.fk_user = u.rowid";
1711 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON s.rowid = p.fk_soc";
1712
1713 // Add table from hooks
1714 $parameters = array();
1715 $reshook = $hookmanager->executeHooks('printFieldListFrom', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
1716 $sql .= $hookmanager->resPrint;
1717 $sql .= " WHERE elementtype = 'task'";
1718 $sql .= " AND p.entity IN (".getEntity('project').")";
1719 if (!$user->hasRight('projet', 'all', 'lire')) {
1720 // Get list of project id allowed to user (in a string list separated by comma)
1721 // TODO This may generate performance trouble when list of project is very large. Solution can be to complete $filterproj with filters on project.
1722 $filterproj = '';
1723 $projectsListId = $projectstatic->getProjectsAuthorizedForUser($user, 0, 1, $user->socid > 0 ? $user->socid : 0, $filterproj);
1724 $sql .= " AND p.rowid IN (".$db->sanitize($projectsListId).")"; // public and assigned to, or restricted to company for external users
1725 }
1726 if (empty($projectidforalltimes) && empty($allprojectforuser)) {
1727 // Limit on one task
1728 $sql .= " AND t.fk_element =".((int) $object->id);
1729 } elseif (!empty($projectidforalltimes)) {
1730 // Limit on one project
1731 $sql .= " AND pt.fk_projet IN (" . $db->sanitize((string) $projectidforalltimes) . ")";
1732 } elseif (!empty($allprojectforuser)) {
1733 // Limit on on user
1734 if (empty($search_user) && !empty($arrayfields['author']['checked'])) {
1735 $search_user = $user->id;
1736 }
1737 if ($search_user > 0) {
1738 $sql .= " AND t.fk_user = " . ((int) $search_user);
1739 }
1740 }
1741
1742 if ($search_note) {
1743 $sql .= natural_search('t.note', $search_note);
1744 }
1745 if ($search_task_ref) {
1746 $sql .= natural_search('pt.ref', $search_task_ref);
1747 }
1748 if (empty($arrayfields['s.name_alias']['checked']) && $search_company) {
1749 $sql .= natural_search(array("s.nom", "s.name_alias"), $search_company);
1750 } else {
1751 if ($search_company) {
1752 $sql .= natural_search('s.nom', $search_company);
1753 }
1754 if ($search_company_alias) {
1755 $sql .= natural_search('s.name_alias', $search_company_alias);
1756 }
1757 }
1758 if ($search_project_ref) {
1759 $sql .= natural_search('p.ref', $search_project_ref);
1760 }
1761 if ($search_project_label) {
1762 $sql .= natural_search('p.title', $search_project_label);
1763 }
1764 if ($search_task_label) {
1765 $sql .= natural_search('pt.label', $search_task_label);
1766 }
1767 if ($search_user > 0) {
1768 $sql .= natural_search('t.fk_user', $search_user, 2);
1769 }
1770 if (!empty($search_product_ref)) {
1771 $sql .= natural_search('prod.ref', $search_product_ref);
1772 }
1773 if ($search_valuebilled == '1') {
1774 $sql .= ' AND t.invoice_id > 0';
1775 }
1776 if ($search_valuebilled == '0') {
1777 $sql .= ' AND (t.invoice_id = 0 OR t.invoice_id IS NULL)';
1778 }
1779
1780 if ($search_date_start) {
1781 $sql .= " AND t.element_date >= '".$db->idate($search_date_start)."'";
1782 }
1783 if ($search_date_end) {
1784 $sql .= " AND t.element_date <= '".$db->idate($search_date_end)."'";
1785 }
1786
1787 if (!empty($arrayfields['t.element_duration']['checked'])) {
1788 if ($search_timespent_starthour || $search_timespent_startmin) {
1789 $timespent_duration_start = $search_timespent_starthour * 60 * 60; // We store duration in seconds
1790 $timespent_duration_start += ($search_timespent_startmin ? $search_timespent_startmin : 0) * 60; // We store duration in seconds
1791 $sql .= " AND t.element_duration >= " . ((int) $timespent_duration_start);
1792 }
1793
1794 if ($search_timespent_endhour || $search_timespent_endmin) {
1795 $timespent_duration_end = $search_timespent_endhour * 60 * 60; // We store duration in seconds
1796 $timespent_duration_end += ($search_timespent_endmin ? $search_timespent_endmin : 0) * 60; // We store duration in seconds
1797 $sql .= " AND t.element_duration <= " . ((int) $timespent_duration_end);
1798 }
1799 }
1800
1801 $sql .= dolSqlDateFilter('t.element_datehour', $search_day, $search_month, $search_year);
1802
1803 // Add where from extra fields
1804 $extrafieldsobjectkey = 'projet_task';
1805 $extrafieldsobjectprefix = 'efpt.';
1806 include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_sql.tpl.php';
1807
1808 // Add where from hooks
1809 $parameters = array();
1810 $reshook = $hookmanager->executeHooks('printFieldListWhere', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
1811 $sql .= $hookmanager->resPrint;
1812
1813 // Count total nb of records
1814 $nbtotalofrecords = '';
1815 if (!getDolGlobalInt('MAIN_DISABLE_FULL_SCANLIST')) {
1816 /* The fast and low memory method to get and count full list converts the sql into a sql count */
1817 $sqlforcount = preg_replace('/^'.preg_quote($sqlfields, '/').'/', 'SELECT COUNT(*) as nbtotalofrecords', $sql);
1818 $sqlforcount = preg_replace('/GROUP BY .*$/', '', $sqlforcount);
1819 $resql = $db->query($sqlforcount);
1820 if ($resql) {
1821 $objforcount = $db->fetch_object($resql);
1822 $nbtotalofrecords = $objforcount->nbtotalofrecords;
1823 } else {
1825 }
1826
1827 if (($page * $limit) > (int) $nbtotalofrecords) { // if total resultset is smaller than the paging size (filtering), goto and load page 0
1828 $page = 0;
1829 $offset = 0;
1830 }
1831 $db->free($resql);
1832 }
1833
1834 // Complete request and execute it with limit
1835 $sql .= $db->order($sortfield, $sortorder);
1836 if ($limit) {
1837 $sql .= $db->plimit($limit + 1, $offset);
1838 }
1839
1840 $resql = $db->query($sql);
1841 if (!$resql) {
1843 exit;
1844 }
1845
1846 $num = $db->num_rows($resql);
1847
1848 if ($num >= 0) {
1849 if (!empty($projectidforalltimes)) {
1850 print '<!-- List of time spent for project -->' . "\n";
1851
1852 $title = $langs->trans("ListTaskTimeUserProject");
1853
1854 print_barre_liste($title, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'clock', 0, $linktocreatetime, '', $limit, 0, 0, 1);
1855 } else {
1856 print '<!-- List of time spent -->' . "\n";
1857
1858 $title = $langs->trans("ListTaskTimeForTask");
1859
1860 print_barre_liste($title, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'clock', 0, $linktocreatetime, '', $limit, 0, 0, 1);
1861 }
1862
1863 $i = 0;
1864 while ($i < $num) {
1865 $obj = $db->fetch_object($resql);
1866
1867 $tasks[$i] = $obj;
1868
1869 $i++;
1870 }
1871 $db->free($resql);
1872 } else {
1874 }
1875
1876 /*
1877 * Form to add a new line of time spent
1878 */
1879 if ($action == 'createtime' && $user->hasRight('projet', 'time')) {
1880 print '<!-- table to add time spent -->' . "\n";
1881 if (!empty($id)) {
1882 print '<input type="hidden" name="taskid" value="' . $id . '">';
1883 }
1884
1885 print '<div class="div-table-responsive-no-min">'; // You can use div-table-responsive-no-min if you don't need reserved height for your table
1886 print '<table class="noborder nohover centpercent">';
1887
1888 print '<tr class="liste_titre">';
1889 print '<td>' . $langs->trans("Date") . '</td>';
1890 if (!empty($allprojectforuser)) {
1891 print '<td>' . $langs->trans("Project") . '</td>';
1892 }
1893 if (empty($id)) {
1894 print '<td>' . $langs->trans("Task") . '</td>';
1895 }
1896 print '<td>' . $langs->trans("By") . '</td>';
1897 print '<td>' . $langs->trans("Note") . '</td>';
1898 print '<td>' . $langs->trans("NewTimeSpent") . '</td>';
1899 print '<td>' . $langs->trans("ProgressDeclared") . '</td>';
1900 if (!getDolGlobalString('PROJECT_HIDE_TASKS') && getDolGlobalString('PROJECT_BILL_TIME_SPENT')) {
1901 print '<td></td>';
1902
1903 if (isModEnabled("service") && !empty($projectstatic->thirdparty) && $projectstatic->thirdparty->id > 0 && $projectstatic->usage_bill_time) {
1904 print '<td>'.$langs->trans("Product").'</td>';
1905 }
1906 }
1907 // Hook fields
1908 $parameters = array('mode' => 'create');
1909 $reshook = $hookmanager->executeHooks('printFieldListTitle', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
1910 print $hookmanager->resPrint;
1911 print '<td></td>';
1912 print "</tr>\n";
1913
1914 print '<tr class="oddeven nohover">';
1915
1916 // Date
1917 print '<td class="maxwidthonsmartphone">';
1918 $newdate = '';
1919 print $form->selectDate($newdate, 'time', ($conf->browser->layout == 'phone' ? 2 : 1), 1, 2, "timespent_date", 1, 0);
1920 print '</td>';
1921
1922 if (!empty($allprojectforuser)) {
1923 print '<td>';
1924 // Add project selector
1925 print '</td>';
1926 }
1927
1928 // Task
1929 $nboftasks = 0;
1930 if (empty($id)) {
1931 print '<td class="maxwidthonsmartphone">';
1932 $nboftasks = $formproject->selectTasks(-1, GETPOSTINT('taskid'), 'taskid', 0, 0, '1', 1, 0, 0, 'maxwidth300', (string) $projectstatic->id, 'progress');
1933 print '</td>';
1934 }
1935
1936 // Contributor
1937 print '<td class="maxwidthonsmartphone nowraponall">';
1938 $contactsofproject = $projectstatic->getListContactId('internal');
1939 if (count($contactsofproject) > 0) {
1940 print img_object('', 'user', 'class="hideonsmartphone"');
1941 if (in_array($user->id, $contactsofproject)) {
1942 $userid = $user->id;
1943 } else {
1944 $userid = $contactsofproject[0];
1945 }
1946
1947 if ($projectstatic->public) {
1948 $contactsofproject = array();
1949 }
1950 print $form->select_dolusers((GETPOSTINT('userid') ? GETPOSTINT('userid') : $userid), 'userid', 0, null, 0, '', $contactsofproject, '0', 0, 0, '', 0, $langs->trans("ResourceNotAssignedToProject"), 'minwidth150imp maxwidth200');
1951 } else {
1952 if ($nboftasks) {
1953 print img_error($langs->trans('FirstAddRessourceToAllocateTime')) . ' ' . $langs->trans('FirstAddRessourceToAllocateTime');
1954 }
1955 }
1956 print '</td>';
1957
1958 // Note
1959 print '<td>';
1960 print '<textarea name="timespent_note" class="maxwidth100onsmartphone" rows="' . ROWS_2 . '">' . (GETPOST('timespent_note') ? GETPOST('timespent_note') : '') . '</textarea>';
1961 print '</td>';
1962
1963 // Duration - Time spent
1964 print '<td class="nowraponall">';
1965 $durationtouse = (GETPOST('timespent_duration') ? GETPOST('timespent_duration') : '');
1966 if (GETPOSTISSET('timespent_durationhour') || GETPOSTISSET('timespent_durationmin')) {
1967 $durationtouse = ((int) GETPOST('timespent_durationhour') * 3600 + (int) GETPOST('timespent_durationmin') * 60);
1968 }
1969 print $form->select_duration('timespent_duration', $durationtouse, 0, 'text');
1970 print '</td>';
1971
1972 // Progress declared
1973 print '<td class="nowrap">';
1974 print $formother->select_percent(GETPOST('progress') ? GETPOST('progress') : $object->progress, 'progress', 0, 5, 0, 100, 1);
1975 print '</td>';
1976
1977 // Billed
1978 if (!getDolGlobalString('PROJECT_HIDE_TASKS') && getDolGlobalString('PROJECT_BILL_TIME_SPENT')) {
1979 print '<td>';
1980 print '</td>';
1981
1982 if (isModEnabled("service") && !empty($projectstatic->thirdparty) && $projectstatic->thirdparty->id > 0 && $projectstatic->usage_bill_time) {
1983 print '<td class="nowraponall">';
1984 print img_picto('', 'service');
1985 print $form->select_produits((GETPOSTISSET('fk_product') ? GETPOSTINT("fk_product") : ''), 'fk_product', 1, 0, $projectstatic->thirdparty->price_level, 1, 2, '', 1, array(), $projectstatic->thirdparty->id, 'None', 0, 'maxwidth150', ($user->hasRight('produit', 'lire') ? 0 : 1), '', null, 1);
1986 print '</td>';
1987 }
1988 }
1989
1990 // Fields from hook
1991 $parameters = array('mode' => 'create');
1992 $reshook = $hookmanager->executeHooks('printFieldListValue', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
1993 print $hookmanager->resPrint;
1994
1995 print '<td class="center">';
1996 $form->buttonsSaveCancel();
1997 print '<input type="submit" name="save" class="button buttongen smallpaddingimp marginleftonly margintoponlyshort marginbottomonlyshort button-add reposition" value="'.$langs->trans("Add").'">';
1998 print '<input type="submit" name="cancel" class="button buttongen smallpaddingimp marginleftonly margintoponlyshort marginbottomonlyshort button-cancel" value="'.$langs->trans("Cancel").'">';
1999 print '</td></tr>';
2000
2001 print '</table>';
2002 print '</div>';
2003
2004 print '<br>';
2005 }
2006
2007 $moreforfilter = '';
2008
2009 $parameters = array();
2010 $reshook = $hookmanager->executeHooks('printFieldPreListTitle', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
2011 if (empty($reshook)) {
2012 $moreforfilter .= $hookmanager->resPrint;
2013 } else {
2014 $moreforfilter = $hookmanager->resPrint;
2015 }
2016
2017 if (!empty($moreforfilter)) {
2018 print '<div class="liste_titre liste_titre_bydiv centpercent">';
2019 print $moreforfilter;
2020 print '</div>';
2021 }
2022
2023 $varpage = empty($contextpage) ? $_SERVER["PHP_SELF"] : $contextpage;
2024 $selectedfields = $form->multiSelectArrayWithCheckbox('selectedfields', $arrayfields, $varpage, $conf->main_checkbox_left_column); // This also change content of $arrayfields
2025 $selectedfields .= (is_array($arrayofmassactions) && count($arrayofmassactions) ? $form->showCheckAddButtons('checkforselect', 1) : '');
2026
2027 // If project has usage "Bill time", we force visibility of field "billed"
2028 if ($projectstatic->usage_bill_time) {
2029 $arrayfields['valuebilled']['checked'] = 1;
2030 }
2031
2032 print '<!-- Liste of time spent -->'."\n";
2033 print '<div class="div-table-responsive">';
2034 print '<table class="tagtable nobottomiftotal liste' . ($moreforfilter ? " listwithfilterbefore" : "") . '">' . "\n";
2035
2036 // Fields title search
2037 // --------------------------------------------------------------------
2038 print '<tr class="liste_titre_filter">';
2039 // Action column
2040 if ($conf->main_checkbox_left_column) {
2041 print '<td class="liste_titre center">';
2042 $searchpicto = $form->showFilterButtons('left');
2043 print $searchpicto;
2044 print '</td>';
2045 }
2046 // Date
2047 if (!empty($arrayfields['t.element_date']['checked'])) {
2048 print '<td class="liste_titre left">';
2049 print '<div class="nowrapfordate paddingright">';
2050 print $form->selectDate($search_date_start ? $search_date_start : -1, 'search_date_start', 0, 0, 1, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans('From'));
2051 print '</div>';
2052 print '<div class="nowrapfordate">';
2053 print $form->selectDate($search_date_end ? $search_date_end : -1, 'search_date_end', 0, 0, 1, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans('to'));
2054 print '</div>';
2055 print '</td>';
2056 }
2057 // Thirdparty
2058 if (!empty($arrayfields['p.fk_soc']['checked'])) {
2059 print '<td class="liste_titre"><input type="text" class="flat maxwidth100" name="$search_company" value="' . dol_escape_htmltag($search_company) . '"></td>';
2060 }
2061
2062 // Thirdparty alias
2063 if (!empty($arrayfields['s.name_alias']['checked'])) {
2064 print '<td class="liste_titre"><input type="text" class="flat maxwidth100" name="$search_company_alias" value="' . dol_escape_htmltag($search_company_alias) . '"></td>';
2065 }
2066
2067 if (!empty($allprojectforuser)) {
2068 if (!empty($arrayfields['p.project_ref']['checked'])) {
2069 print '<td class="liste_titre"><input type="text" class="flat maxwidth100" name="$search_project_ref" value="' . dol_escape_htmltag($search_project_ref) . '"></td>';
2070 }
2071 if (!empty($arrayfields['p.project_label']['checked'])) {
2072 print '<td class="liste_titre"><input type="text" class="flat maxwidth100" name="$search_project_label" value="' . dol_escape_htmltag($search_project_label) . '"></td>';
2073 }
2074 }
2075 // Task
2076 if ((empty($id) && empty($ref)) || !empty($projectidforalltimes)) { // Not a dedicated task
2077 if (!empty($arrayfields['t.element_ref']['checked'])) {
2078 print '<td class="liste_titre"><input type="text" class="flat maxwidth75" name="search_task_ref" value="'.dol_escape_htmltag($search_task_ref).'"></td>';
2079 }
2080 if (!empty($arrayfields['t.element_label']['checked'])) {
2081 print '<td class="liste_titre"><input type="text" class="flat maxwidth100" name="search_task_label" value="'.dol_escape_htmltag($search_task_label).'"></td>';
2082 }
2083 }
2084 // Author
2085 if (!empty($arrayfields['author']['checked'])) {
2086 print '<td class="liste_titre">'.$form->select_dolusers(($search_user > 0 ? $search_user : -1), 'search_user', 1, null, 0, '', '', '0', 0, 0, '', 0, '', 'maxwidth125').'</td>';
2087 }
2088 // Note
2089 if (!empty($arrayfields['t.note']['checked'])) {
2090 print '<td class="liste_titre"><input type="text" class="flat maxwidth75" name="search_note" value="' . dol_escape_htmltag($search_note) . '"></td>';
2091 }
2092 // Last modification
2093 if (!empty($arrayfields['t.tms']['checked'])) {
2094 print '<td class="liste_titre"></td>';
2095 }
2096 // Duration
2097 if (!empty($arrayfields['t.element_duration']['checked'])) {
2098 // Duration - Time spent
2099 print '<td class="liste_titre right">';
2100
2101 $durationtouse_start = '';
2102 if ($search_timespent_starthour || $search_timespent_startmin) {
2103 $durationtouse_start = ($search_timespent_starthour * 3600 + $search_timespent_startmin * 60);
2104 }
2105 print '<div class="nowraponall"><span class="opacitymedium">' . $langs->trans('fromhour') . '</span> ';
2106 print $form->select_duration('search_timespent_duration_start', $durationtouse_start, 0, 'text', 0, 1);
2107 print '</div>';
2108
2109 $durationtouse_end = '';
2110 if ($search_timespent_endhour || $search_timespent_endmin) {
2111 $durationtouse_end = ($search_timespent_endhour * 3600 + $search_timespent_endmin * 60);
2112 }
2113 print '<div class="nowraponall"><span class="opacitymedium">' . $langs->trans('tohour') . '</span> ';
2114 print $form->select_duration('search_timespent_duration_end', $durationtouse_end, 0, 'text', 0, 1);
2115 print '</div>';
2116
2117 print '</td>';
2118 }
2119 // Product
2120 if (!empty($arrayfields['t.fk_product']['checked'])) {
2121 print '<td class="liste_titre"></td>';
2122 }
2123 // Value in main currency
2124 if (!empty($arrayfields['value']['checked'])) {
2125 print '<td class="liste_titre"></td>';
2126 }
2127 // Value billed
2128 if (!empty($arrayfields['valuebilled']['checked'])) {
2129 print '<td class="liste_titre center">' . $form->selectyesno('search_valuebilled', $search_valuebilled, 1, false, 1) . '</td>';
2130 }
2131
2132 // Extra fields
2133 $extrafieldsobjectkey = 'projet_task';
2134 $extrafieldsobjectprefix = 'ef.';
2135 include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_input.tpl.php';
2136 // Fields from hook
2137 $parameters = array('arrayfields' => $arrayfields);
2138 $reshook = $hookmanager->executeHooks('printFieldListOption', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
2139 print $hookmanager->resPrint;
2140 // Action column
2141 if (!$conf->main_checkbox_left_column) {
2142 print '<td class="liste_titre center">';
2143 $searchpicto = $form->showFilterButtons();
2144 print $searchpicto;
2145 print '</td>';
2146 }
2147 print '</tr>' . "\n";
2148
2149
2150 $totalarray = array();
2151 $totalarray['nbfield'] = 0;
2152
2153 // Fields title label
2154 // --------------------------------------------------------------------
2155 print '<tr class="liste_titre">';
2156 if ($conf->main_checkbox_left_column) {
2157 print_liste_field_titre($selectedfields, $_SERVER["PHP_SELF"], "", '', $param, '', $sortfield, $sortorder, 'center maxwidthsearch ');
2158 $totalarray['nbfield']++;
2159 }
2160 if (!empty($arrayfields['t.element_date']['checked'])) {
2161 print_liste_field_titre($arrayfields['t.element_date']['label'], $_SERVER['PHP_SELF'], 't.element_date,t.element_datehour,t.rowid', '', $param, '', $sortfield, $sortorder);
2162 $totalarray['nbfield']++;
2163 }
2164 if (!empty($arrayfields['p.fk_soc']['checked'])) {
2165 print_liste_field_titre($arrayfields['p.fk_soc']['label'], $_SERVER['PHP_SELF'], 't.element_date,t.element_datehour,t.rowid', '', $param, '', $sortfield, $sortorder);
2166 $totalarray['nbfield']++;
2167 }
2168 if (!empty($arrayfields['s.name_alias']['checked'])) {
2169 // @phan-suppress-next-line PhanTypeInvalidDimOffset
2170 print_liste_field_titre($arrayfields['s.name_alias']['label'], $_SERVER['PHP_SELF'], 's.name_alias', '', $param, '', $sortfield, $sortorder);
2171 $totalarray['nbfield']++;
2172 }
2173 if (!empty($allprojectforuser)) {
2174 if (!empty($arrayfields['p.project_ref']['checked'])) {
2175 print_liste_field_titre($arrayfields['p.project_ref']['label'], $_SERVER['PHP_SELF'], 'p.ref', '', $param, '', $sortfield, $sortorder);
2176 $totalarray['nbfield']++;
2177 }
2178 if (!empty($arrayfields['p.project_label']['checked'])) {
2179 print_liste_field_titre($arrayfields['p.project_label']['label'], $_SERVER['PHP_SELF'], 'p.title', '', $param, '', $sortfield, $sortorder);
2180 $totalarray['nbfield']++;
2181 }
2182 }
2183 if ((empty($id) && empty($ref)) || !empty($projectidforalltimes)) { // Not a dedicated task
2184 if (!empty($arrayfields['t.element_ref']['checked'])) {
2185 print_liste_field_titre($arrayfields['t.element_ref']['label'], $_SERVER['PHP_SELF'], 'pt.ref', '', $param, '', $sortfield, $sortorder);
2186 $totalarray['nbfield']++;
2187 }
2188 if (!empty($arrayfields['t.element_label']['checked'])) {
2189 print_liste_field_titre($arrayfields['t.element_label']['label'], $_SERVER['PHP_SELF'], 'pt.label', '', $param, '', $sortfield, $sortorder);
2190 $totalarray['nbfield']++;
2191 }
2192 }
2193 if (!empty($arrayfields['author']['checked'])) {
2194 print_liste_field_titre($arrayfields['author']['label'], $_SERVER['PHP_SELF'], '', '', $param, '', $sortfield, $sortorder);
2195 $totalarray['nbfield']++;
2196 }
2197 if (!empty($arrayfields['t.note']['checked'])) {
2198 print_liste_field_titre($arrayfields['t.note']['label'], $_SERVER['PHP_SELF'], 't.note', '', $param, '', $sortfield, $sortorder);
2199 $totalarray['nbfield']++;
2200 }
2201 if (!empty($arrayfields['t.tms']['checked'])) {
2202 print_liste_field_titre($arrayfields['t.tms']['label'], $_SERVER['PHP_SELF'], 't.tms', '', $param, '', $sortfield, $sortorder);
2203 $totalarray['nbfield']++;
2204 }
2205 if (!empty($arrayfields['t.element_duration']['checked'])) {
2206 print_liste_field_titre($arrayfields['t.element_duration']['label'], $_SERVER['PHP_SELF'], 't.element_duration', '', $param, '', $sortfield, $sortorder, 'right ');
2207 $totalarray['nbfield']++;
2208 }
2209 if (!empty($arrayfields['t.fk_product']['checked'])) {
2210 print_liste_field_titre($arrayfields['t.fk_product']['label'], $_SERVER['PHP_SELF'], 't.fk_product', '', $param, '', $sortfield, $sortorder);
2211 $totalarray['nbfield']++;
2212 }
2213
2214 if (!empty($arrayfields['value']['checked'])) {
2215 print_liste_field_titre($arrayfields['value']['label'], $_SERVER['PHP_SELF'], '', '', $param, '', $sortfield, $sortorder, 'right ');
2216 $totalarray['nbfield']++;
2217 }
2218 if (!empty($arrayfields['valuebilled']['checked'])) {
2219 print_liste_field_titre($arrayfields['valuebilled']['label'], $_SERVER['PHP_SELF'], 'il.total_ht', '', $param, '', $sortfield, $sortorder, 'center ', $langs->trans("SelectLinesOfTimeSpentToInvoice"));
2220 $totalarray['nbfield']++;
2221 }
2222 // Extra fields
2223 $extrafieldsobjectkey = 'projet_task';
2224 $extrafieldsobjectprefix = 'ef.';
2225 include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_title.tpl.php';
2226 // Hook fields
2227 $parameters = array('arrayfields' => $arrayfields, 'param' => $param, 'sortfield' => $sortfield, 'sortorder' => $sortorder);
2228 $reshook = $hookmanager->executeHooks('printFieldListTitle', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
2229 print $hookmanager->resPrint;
2230 if (!$conf->main_checkbox_left_column) {
2231 print_liste_field_titre($selectedfields, $_SERVER["PHP_SELF"], "", '', $param, '', $sortfield, $sortorder, 'center maxwidthsearch ');
2232 $totalarray['nbfield']++;
2233 }
2234 print "</tr>\n";
2235
2236 $tasktmp = new Task($db);
2237 $tmpinvoice = new Facture($db);
2238
2239 if ($page) {
2240 $param .= '&page='.((int) $page);
2241 }
2242 $param .= '&sortfield='.urlencode($sortfield).'&sortorder='.urlencode($sortorder);
2243
2244 $i = 0;
2245
2246 $savnbfield = $totalarray['nbfield'];
2247 $totalarray = array();
2248 $totalarray['nbfield'] = 0;
2249 //$imaxinloop = ($limit ? min($num, $limit) : $num);
2250 foreach ($tasks as $task_time) {
2251 if ($i >= $limit) {
2252 break;
2253 }
2254
2255 // Line is invoiced if it has an invoice_id
2256 $invoiced = $task_time->invoice_id ? true : false;
2257
2258 $date1 = $db->jdate($task_time->element_date);
2259 $date2 = $db->jdate($task_time->element_datehour);
2260
2261 // Show here line of result
2262 $j = 0;
2263 print '<tr data-rowid="'.$task_time->rowid.'" class="oddeven">';
2264
2265 // Action column
2266 if ($conf->main_checkbox_left_column) {
2267 print '<td class="center nowraponall">';
2268 if (($action == 'editline' || $action == 'splitline') && GETPOSTINT('lineid') == $task_time->rowid) {
2269 print '<input type="hidden" name="lineid" value="' . GETPOSTINT('lineid') . '">';
2270 if ($id) {
2271 print '<input type="hidden" name="id" value="' . $id . '">'; // If you enable this, the edit will go beack to task view
2272 }
2273 print '<input type="submit" class="button buttongen reposition smallpaddingimp margintoponlyshort marginbottomonlyshort button-save" name="save" value="'.$langs->trans("Save").'">';
2274 print '<br>';
2275 print '<input type="submit" class="button buttongen reposition smallpaddingimp margintoponlyshort marginbottomonlyshort button-cancel" name="cancel" value="'.$langs->trans("Cancel").'">';
2276 } elseif ($user->hasRight('projet', 'time') || $user->hasRight('projet', 'all', 'creer')) { // Read project and enter time consumed on assigned tasks
2277 if (in_array($task_time->fk_user, $childids) || $user->hasRight('projet', 'all', 'creer')) {
2278 print '<a class="reposition editfielda" href="'.$_SERVER["PHP_SELF"].'?'.($withproject ? 'id='.$task_time->fk_element : '').'&action=editline&token='.newToken().'&lineid='.$task_time->rowid.$param.((empty($id) || $tab == 'timespent') ? '&tab=timespent' : '').'">';
2279 print img_edit('default', 0, 'class="pictofixedwidth paddingleft"');
2280 print '</a>';
2281
2282 if (getDolGlobalString('PROJECT_ALLOW_SPLIT_TIMESPENT')) {
2283 print '<a class="reposition editfielda paddingleft" href="' . $_SERVER["PHP_SELF"] . '?action=splitline&token=' . newToken() . '&lineid=' . $task_time->rowid . $param . ((empty($id) || $tab == 'timespent') ? '&tab=timespent' : '') . '">';
2284 print img_split('', 'class="pictofixedwidth paddingleft"');
2285 print '</a>';
2286 }
2287
2288 print '<a class="reposition paddingleft" href="'.$_SERVER["PHP_SELF"].'?'.($withproject ? 'id='.$task_time->fk_element : '').'&action=deleteline&token='.newToken().'&lineid='.$task_time->rowid.$param.((empty($id) || $tab == 'timespent') ? '&tab=timespent' : '').'">';
2289 print img_delete('default', 'class="pictodelete paddingleft"');
2290 print '</a>';
2291
2292 if ($massactionbutton || $massaction) { // If we are in select mode (massactionbutton defined) or if we have already selected and sent an action ($massaction) defined
2293 $selected = 0;
2294 if (in_array($task_time->rowid, $arrayofselected)) {
2295 $selected = 1;
2296 }
2297 print '&nbsp;';
2298
2299 // Disable select if task not billable or already invoiced
2300 $disabled = (intval($task_time->billable) != 1 || $invoiced);
2301 $ctrl = '<input '.($disabled ? 'disabled' : '').' id="cb' . $task_time->rowid . '" class="flat checkforselect marginleftonly" type="checkbox" name="toselect[]" value="' . $task_time->rowid . '"' . ($selected ? ' checked="checked"' : '') . '>';
2302 if ($disabled) {
2303 // If disabled, a dbl-click very close outside the control
2304 // will re-enable it, so that user is not blocked if needed.
2305 print '<span id="cbsp'. $task_time->rowid . '">'.$ctrl.'</span>';
2306 print '<script>$("#cbsp' . $task_time->rowid . '").dblclick(()=>{ $("#cb' . $task_time->rowid . '").removeAttr("disabled") })</script>';
2307 } else {
2308 print $ctrl;
2309 }
2310 }
2311 }
2312 }
2313 print '</td>';
2314 if (!$i) {
2315 $totalarray['nbfield']++;
2316 }
2317 }
2318
2319 // Date
2320 if (!empty($arrayfields['t.element_date']['checked'])) {
2321 print '<td class="nowrap">';
2322 if ($action == 'editline' && GETPOSTINT('lineid') == $task_time->rowid) {
2323 if (empty($task_time->element_date_withhour)) {
2324 print $form->selectDate(($date2 ? $date2 : $date1), 'timeline', 4, 3, 2, "timespent_date", 1, 0);
2325 } else {
2326 print $form->selectDate(($date2 ? $date2 : $date1), 'timeline', 2, 1, 2, "timespent_date", 1, 0);
2327 }
2328 } else {
2329 print dol_print_date(($date2 ? $date2 : $date1), ($task_time->element_date_withhour ? 'dayhour' : 'day'));
2330 }
2331 print '</td>';
2332 if (!$i) {
2333 $totalarray['nbfield']++;
2334 }
2335 }
2336
2337 // Thirdparty
2338 if (!empty($arrayfields['p.fk_soc']['checked'])) {
2339 print '<td class="tdoverflowmax125">';
2340 if ($task_time->fk_soc > 0) {
2341 if (empty($conf->cache['thirdparty'][$task_time->fk_soc])) {
2342 $tmpsociete = new Societe($db);
2343 $tmpsociete->fetch($task_time->fk_soc);
2344 $conf->cache['thirdparty'][$task_time->fk_soc] = $tmpsociete;
2345 } else {
2346 $tmpsociete = $conf->cache['thirdparty'][$task_time->fk_soc];
2347 }
2348 print $tmpsociete->getNomUrl(1, '', 100, 0, 1, empty($arrayfields['s.name_alias']['checked']) ? 0 : 1);
2349 }
2350 print '</td>';
2351 if (!$i) {
2352 $totalarray['nbfield']++;
2353 }
2354 }
2355
2356 // Thirdparty alias
2357 if (!empty($arrayfields['s.name_alias']['checked'])) {
2358 $valtoshow = '';
2359 if ($task_time->fk_soc > 0) {
2360 if (empty($conf->cache['thirdparty'][$task_time->fk_soc])) {
2361 $tmpsociete = new Societe($db);
2362 $tmpsociete->fetch($task_time->fk_soc);
2363 $conf->cache['thirdparty'][$task_time->fk_soc] = $tmpsociete;
2364 } else {
2365 $tmpsociete = $conf->cache['thirdparty'][$task_time->fk_soc];
2366 }
2367 $valtoshow = $tmpsociete->name_alias;
2368 }
2369 print '<td class="nowrap tdoverflowmax150" title="'.dol_escape_htmltag($valtoshow).'">';
2370 print $valtoshow;
2371 print '</td>';
2372 if (!$i) {
2373 $totalarray['nbfield']++;
2374 }
2375 }
2376
2377 if (!empty($allprojectforuser)) {
2378 // Project ref
2379 if (!empty($arrayfields['p.project_ref']['checked'])) {
2380 if (empty($conf->cache['project'][$task_time->fk_projet])) {
2381 $tmpproject = new Project($db);
2382 $tmpproject->fetch($task_time->fk_projet);
2383 $conf->cache['project'][$task_time->fk_projet] = $tmpproject;
2384 } else {
2385 $tmpproject = $conf->cache['project'][$task_time->fk_projet];
2386 }
2387 print '<td class="nowraponall">';
2388 print $tmpproject->getNomUrl(1);
2389 print '</td>';
2390 if (!$i) {
2391 $totalarray['nbfield']++;
2392 }
2393 }
2394 // Project label
2395 if (!empty($arrayfields['p.project_label']['checked'])) {
2396 if (empty($conf->cache['project'][$task_time->fk_projet])) {
2397 $tmpproject = new Project($db);
2398 $tmpproject->fetch($task_time->fk_projet);
2399 $conf->cache['project'][$task_time->fk_projet] = $tmpproject;
2400 } else {
2401 $tmpproject = $conf->cache['project'][$task_time->fk_projet];
2402 }
2403 print '<td class="tdoverflowmax250" title="'.dolPrintHTMLForAttribute($tmpproject->title).'">';
2404 if (!empty($arrayfields['p.project_ref']['checked'])) {
2405 print dolPrintHTML($tmpproject->title);
2406 } else {
2407 print $tmpproject->getNomUrl(1, '', -1);
2408 }
2409 print '</td>';
2410 if (!$i) {
2411 $totalarray['nbfield']++;
2412 }
2413 }
2414 }
2415
2416 // Task ref
2417 if (!empty($arrayfields['t.element_ref']['checked'])) {
2418 if ((empty($id) && empty($ref)) || !empty($projectidforalltimes)) { // Not a dedicated task
2419 print '<td class="nowrap">';
2420 if ($action == 'editline' && GETPOSTINT('lineid') == $task_time->rowid) {
2421 $formproject->selectTasks(-1, GETPOSTINT('taskid') ? GETPOSTINT('taskid') : $task_time->fk_element, 'taskid', 0, 0, '1', 1, 0, 0, 'maxwidth250', (string) $projectstatic->id, '');
2422 } else {
2423 $tasktmp->id = $task_time->fk_element;
2424 $tasktmp->ref = $task_time->ref;
2425 $tasktmp->label = $task_time->label;
2426 print $tasktmp->getNomUrl(1, 'withproject', 'time');
2427 }
2428 print '</td>';
2429 if (!$i) {
2430 $totalarray['nbfield']++;
2431 }
2432 }
2433 } elseif ($action !== 'createtime') {
2434 print '<input type="hidden" name="taskid" value="' . $id . '">';
2435 }
2436
2437 // Task label
2438 if (!empty($arrayfields['t.element_label']['checked'])) {
2439 if ((empty($id) && empty($ref)) || !empty($projectidforalltimes)) { // Not a dedicated task
2440 print '<td class="tdoverflowmax250" title="'.dolPrintHTMLForAttribute($task_time->label).'">';
2441 if (!empty($arrayfields['t.element_ref']['checked'])) {
2442 print dolPrintHTML($task_time->label);
2443 } else {
2444 $tasktmp->id = $task_time->fk_element;
2445 $tasktmp->ref = $task_time->ref;
2446 $tasktmp->label = $task_time->label;
2447 print $tasktmp->getNomUrl(1, 'withproject', 'time', -1);
2448 }
2449 print '</td>';
2450 if (!$i) {
2451 $totalarray['nbfield']++;
2452 }
2453 }
2454 }
2455
2456 // User
2457 if (!empty($arrayfields['author']['checked'])) {
2458 print '<td class="tdoverflowmax125">';
2459 if ($action == 'editline' && GETPOSTINT('lineid') == $task_time->rowid) {
2460 // Here $object is task. TODO Add a cache
2461 if (empty($object->id)) {
2462 $object->fetch($task_time->fk_element);
2463 }
2464 $contactsoftask = $object->getListContactId('internal');
2465 if (!in_array($task_time->fk_user, $contactsoftask)) {
2466 $contactsoftask[] = $task_time->fk_user;
2467 }
2468 if (count($contactsoftask) > 0) {
2469 print img_object('', 'user', 'class="pictofixedwidth hideonsmartphone"');
2470 print $form->select_dolusers($task_time->fk_user, 'userid_line', 0, null, 0, '', $contactsoftask, '0', 0, 0, '', 0, '', 'minwidth100 maxwidth100'); // maxwidth must be lowed than minwidth of the td
2471 } else {
2472 print img_error($langs->trans('FirstAddRessourceToAllocateTime')) . $langs->trans('FirstAddRessourceToAllocateTime');
2473 }
2474 } else {
2475 $userstatic->id = $task_time->fk_user;
2476 $userstatic->lastname = $task_time->lastname;
2477 $userstatic->firstname = $task_time->firstname;
2478 $userstatic->photo = $task_time->photo;
2479 $userstatic->gender = $task_time->gender;
2480 $userstatic->status = $task_time->user_status;
2481
2482 print $userstatic->getNomUrl(-1);
2483 }
2484 print '</td>';
2485 if (!$i) {
2486 $totalarray['nbfield']++;
2487 }
2488 }
2489
2490 // Note
2491 if (!empty($arrayfields['t.note']['checked'])) {
2492 if ($action == 'editline' && GETPOSTINT('lineid') == $task_time->rowid) {
2493 print '<td class="small">';
2494 print '<textarea name="timespent_note_line" class="centpercentimp" rows="' . ROWS_2 . '">' . dol_escape_htmltag($task_time->note, 0, 1) . '</textarea>';
2495 print '</td>';
2496 } else {
2497 print '<td class="small tdoverflowmax150 classfortooltip" title="'.dol_string_onlythesehtmltags(dol_htmlentitiesbr($task_time->note)).'">';
2498 print dolGetFirstLineOfText($task_time->note);
2499 print '</td>';
2500 }
2501 if (!$i) {
2502 $totalarray['nbfield']++;
2503 }
2504 } elseif ($action == 'editline' && GETPOSTINT('lineid') == $task_time->rowid) {
2505 print '<input type="hidden" name="timespent_note_line" value="' . dol_escape_htmltag($task_time->note, 0, 1) . '">';
2506 }
2507
2508 // Last modification
2509 if (!empty($arrayfields['t.tms']['checked'])) {
2510 print '<td class="nowraponall">';
2511 print dol_print_date($task_time->tms, 'dayhour');
2512 print '</td>';
2513 if (!$i) {
2514 $totalarray['nbfield']++;
2515 }
2516 }
2517
2518 // Time spent - Duration
2519 if (!empty($arrayfields['t.element_duration']['checked'])) {
2520 print '<td class="right nowraponall">';
2521 if ($action == 'editline' && GETPOSTINT('lineid') == $task_time->rowid) {
2522 print '<input type="hidden" name="old_duration" value="'.$task_time->element_duration.'">';
2523 print $form->select_duration('new_duration', $task_time->element_duration, 0, 'text');
2524 } else {
2525 print convertSecondToTime($task_time->element_duration, 'allhourmin');
2526 }
2527 print '</td>';
2528 if (!$i) {
2529 $totalarray['nbfield']++;
2530 }
2531 if (!$i) {
2532 $totalarray['pos'][$totalarray['nbfield']] = 't.element_duration';
2533 }
2534 if (empty($totalarray['val']['t.element_duration'])) {
2535 $totalarray['val']['t.element_duration'] = $task_time->element_duration;
2536 } else {
2537 $totalarray['val']['t.element_duration'] += $task_time->element_duration;
2538 }
2539 if (!$i) {
2540 $totalarray['totaldurationfield'] = $totalarray['nbfield'];
2541 }
2542 if (empty($totalarray['totalduration'])) {
2543 $totalarray['totalduration'] = $task_time->element_duration;
2544 } else {
2545 $totalarray['totalduration'] += $task_time->element_duration;
2546 }
2547 }
2548
2549 // Product
2550 if (!empty($arrayfields['t.fk_product']['checked'])) {
2551 print '<td class="nowraponall">';
2552 if ($action == 'editline' && GETPOSTINT('lineid') == $task_time->rowid) {
2553 print img_picto('', 'service');
2554 print $form->select_produits($task_time->fk_product, 'fk_product', 1, 0, $projectstatic->thirdparty->price_level, 1, 2, '', 1, array(), $projectstatic->thirdparty->id, 'None', 0, 'maxwidth500', ($user->hasRight('produit', 'lire') ? 0 : 1), '', null, 1);
2555 } elseif (!empty($task_time->fk_product)) {
2556 $product = new Product($db);
2557 $resultFetch = $product->fetch($task_time->fk_product);
2558 if ($resultFetch < 0) {
2559 setEventMessages($product->error, $product->errors, 'errors');
2560 } else {
2561 print $product->getNomUrl(1);
2562 }
2563 }
2564 print '</td>';
2565 if (!$i) {
2566 $totalarray['nbfield']++;
2567 }
2568 }
2569
2570 // Value spent
2571 if (!empty($arrayfields['value']['checked'])) {
2572 $langs->load("salaries");
2573 $value = price2num($task_time->thm * $task_time->element_duration / 3600, 'MT', 1);
2574
2575 print '<td class="nowraponall right">';
2576 print '<span class="amount" title="' . $langs->trans("THM") . ': ' . price($task_time->thm) . '">';
2577 print price($value, 1, $langs, 1, -1, -1, $conf->currency);
2578 print '</span>';
2579 print '</td>';
2580 if (!$i) {
2581 $totalarray['nbfield']++;
2582 }
2583 if (!$i) {
2584 $totalarray['pos'][$totalarray['nbfield']] = 'value';
2585 }
2586 if (empty($totalarray['val']['value'])) {
2587 $totalarray['val']['value'] = $value;
2588 } else {
2589 $totalarray['val']['value'] += $value;
2590 }
2591 if (!$i) {
2592 $totalarray['totalvaluefield'] = $totalarray['nbfield'];
2593 }
2594 if (empty($totalarray['totalvalue'])) {
2595 $totalarray['totalvalue'] = $value;
2596 } else {
2597 $totalarray['totalvalue'] += $value;
2598 }
2599 }
2600
2601 // Value billed
2602 if (!empty($arrayfields['valuebilled']['checked'])) {
2603 print '<td class="center">'; // invoice_id and invoice_line_id
2604 if (!getDolGlobalString('PROJECT_HIDE_TASKS') && getDolGlobalString('PROJECT_BILL_TIME_SPENT')) {
2605 if ($projectstatic->usage_bill_time) {
2606 if ($task_time->invoice_id) {
2607 $result = $tmpinvoice->fetch($task_time->invoice_id);
2608 if ($result > 0) {
2609 if ($user->hasRight('facture', 'lire')) {
2610 if ($action == 'editline' && GETPOSTINT('lineid') == $task_time->rowid) {
2611 print $formproject->selectInvoiceAndLine($task_time->invoice_id, $task_time->invoice_line_id, 'invoiceid', 'invoicelineid', 'maxwidth500', array('p.rowid' => $projectstatic->id));
2612 } else {
2613 print $tmpinvoice->getNomUrl(1);
2614 if (!empty($task_time->invoice_line_id)) {
2615 $invoiceLine = new FactureLigne($db);
2616 $invoiceLine->fetch($task_time->invoice_line_id);
2617 if (!empty($invoiceLine->id)) {
2618 print '<br><span class="small opacitymedium">'.$langs->trans('Qty').':'.$invoiceLine->qty;
2619 print ' '.$langs->trans('TotalHT').':'.price($invoiceLine->total_ht);
2620 print '</span>';
2621 }
2622 }
2623 }
2624 } else {
2625 print $langs->trans("Yes");
2626 }
2627 }
2628 $invoiced = true;
2629 } else {
2630 if (intval($task_time->billable) == 1) {
2631 print $langs->trans("No");
2632 } else {
2633 print $langs->trans("Disabled");
2634 }
2635 }
2636 } else {
2637 print '<span class="opacitymedium">' . $langs->trans("NA") . '</span>';
2638 }
2639 }
2640 print '</td>';
2641 if (!$i) {
2642 $totalarray['nbfield']++;
2643 }
2644 }
2645
2646 // Extra fields
2647 $obj = $task_time;
2648 $extrafieldsobjectkey = 'projet_task';
2649 $extrafieldsobjectprefix = 'ef.';
2650 include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_print_fields.tpl.php';
2651
2652 // Fields from hook
2653 $parameters = array('arrayfields' => $arrayfields, 'obj' => $task_time, 'i' => $i, 'totalarray' => &$totalarray);
2654 $reshook = $hookmanager->executeHooks('printFieldListValue', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
2655 print $hookmanager->resPrint;
2656
2657 // Action column
2658 if (!$conf->main_checkbox_left_column) {
2659 print '<td class="center nowraponall">';
2660 if (($action == 'editline' || $action == 'splitline') && GETPOSTINT('lineid') == $task_time->rowid) {
2661 print '<input type="hidden" name="lineid" value="'.GETPOSTINT('lineid').'">';
2662 print '<input type="submit" class="button buttongen smallpaddingimp margintoponlyshort marginbottomonlyshort button-save" name="save" value="'.$langs->trans("Save").'">';
2663 print '<br>';
2664 print '<input type="submit" class="button buttongen smallpaddingimp margintoponlyshort marginbottomonlyshort button-cancel" name="cancel" value="'.$langs->trans("Cancel").'">';
2665 } elseif ($user->hasRight('projet', 'time') || $user->hasRight('projet', 'all', 'creer')) { // Read project and enter time consumed on assigned tasks
2666 if (in_array($task_time->fk_user, $childids) || $user->hasRight('projet', 'all', 'creer')) {
2667 print '<a class="reposition editfielda" href="'.$_SERVER["PHP_SELF"].'?'.($withproject ? 'id='.$task_time->fk_element : '').'&action=editline&token='.newToken().'&lineid='.$task_time->rowid.$param.((empty($id) || $tab == 'timespent') ? '&tab=timespent' : '').'">';
2668 print img_edit('default', 0, 'class="pictofixedwidth paddingleft"');
2669 print '</a>';
2670
2671 if (getDolGlobalString('PROJECT_ALLOW_SPLIT_TIMESPENT')) {
2672 print '<a class="reposition editfielda paddingleft" href="' . $_SERVER["PHP_SELF"] . '?action=splitline&token=' . newToken() . '&lineid=' . $task_time->rowid . $param . ((empty($id) || $tab == 'timespent') ? '&tab=timespent' : '') . '">';
2673 print img_split('', 'class="pictofixedwidth paddingleft"');
2674 print '</a>';
2675 }
2676
2677 print '<a class="reposition paddingleft" href="'.$_SERVER["PHP_SELF"].'?'.($withproject ? 'id='.$task_time->fk_element : '').'&action=deleteline&token='.newToken().'&lineid='.$task_time->rowid.$param.((empty($id) || $tab == 'timespent') ? '&tab=timespent' : '').'">';
2678 print img_delete('default', 'class="pictodelete paddingleft"');
2679 print '</a>';
2680
2681 if ($massactionbutton || $massaction) { // If we are in select mode (massactionbutton defined) or if we have already selected and sent an action ($massaction) defined
2682 $selected = 0;
2683 if (in_array($task_time->rowid, $arrayofselected)) {
2684 $selected = 1;
2685 }
2686 print '&nbsp;';
2687
2688 // Disable select if task not billable or already invoiced
2689 $disabled = (intval($task_time->billable) != 1 || $invoiced);
2690 $ctrl = '<input '.($disabled ? 'disabled' : '').' id="cb' . $task_time->rowid . '" class="flat checkforselect marginleftonly" type="checkbox" name="toselect[]" value="' . $task_time->rowid . '"' . ($selected ? ' checked="checked"' : '') . '>';
2691 if ($disabled) {
2692 // If disabled, a dbl-click very close outside the control
2693 // will re-enable it, so that user is not blocked if needed.
2694 print '<span id="cbsp'. $task_time->rowid . '">'.$ctrl.'</span>';
2695 print '<script>$("#cbsp' . $task_time->rowid . '").dblclick(()=>{ $("#cb' . $task_time->rowid . '").removeAttr("disabled") })</script>';
2696 } else {
2697 print $ctrl;
2698 }
2699 }
2700 }
2701 }
2702 print '</td>';
2703 if (!$i) {
2704 $totalarray['nbfield']++;
2705 }
2706 }
2707
2708 print "</tr>\n";
2709
2710
2711 // Add the lines for the split feature
2712
2713 if ($action == 'splitline' && GETPOSTINT('lineid') == $task_time->rowid) {
2714 print '<!-- first line -->';
2715 print '<tr class="oddeven">';
2716
2717 // Action column
2718 if ($conf->main_checkbox_left_column) {
2719 print '<td></td>';
2720 }
2721
2722 // Date
2723 if (!empty($arrayfields['t.element_date']['checked'])) {
2724 print '<td class="nowrap">';
2725 if ($action == 'splitline' && GETPOSTINT('lineid') == $task_time->rowid) {
2726 if (empty($task_time->element_date_withhour)) {
2727 print $form->selectDate(($date2 ? $date2 : $date1), 'timeline', 3, 3, 2, "timespent_date", 1, 0);
2728 } else {
2729 print $form->selectDate(($date2 ? $date2 : $date1), 'timeline', 1, 1, 2, "timespent_date", 1, 0);
2730 }
2731 } else {
2732 print dol_print_date(($date2 ? $date2 : $date1), ($task_time->element_date_withhour ? 'dayhour' : 'day'));
2733 }
2734 print '</td>';
2735 }
2736
2737 // Thirdparty
2738 if (!empty($arrayfields['p.fk_soc']['checked'])) {
2739 print '<td class="nowrap">';
2740 print '</td>';
2741 }
2742
2743 // Thirdparty alias
2744 if (!empty($arrayfields['s.name_alias']['checked'])) {
2745 print '<td class="nowrap">';
2746 print '</td>';
2747 }
2748
2749 // Project ref
2750 if (!empty($allprojectforuser)) {
2751 if ((empty($id) && empty($ref)) || !empty($projectidforalltimes)) { // Not a dedicated task
2752 print '<td class="nowrap">';
2753 print '</td>';
2754 }
2755 }
2756
2757 // Task ref
2758 if (!empty($arrayfields['t.element_ref']['checked'])) {
2759 if ((empty($id) && empty($ref)) || !empty($projectidforalltimes)) { // Not a dedicated task
2760 print '<td class="nowrap">';
2761 $tasktmp->id = $task_time->fk_element;
2762 $tasktmp->ref = $task_time->ref;
2763 $tasktmp->label = $task_time->label;
2764 print $tasktmp->getNomUrl(1, 'withproject', 'time');
2765 print '</td>';
2766 }
2767 }
2768
2769 // Task label
2770 if (!empty($arrayfields['t.element_label']['checked'])) {
2771 if ((empty($id) && empty($ref)) || !empty($projectidforalltimes)) { // Not a dedicated task
2772 print '<td class="tdoverflowmax300" title="'.dol_escape_htmltag($task_time->label).'">';
2773 print dol_escape_htmltag($task_time->label);
2774 print '</td>';
2775 }
2776 }
2777
2778 // User
2779 if (!empty($arrayfields['author']['checked'])) {
2780 print '<td class="nowraponall">';
2781 if ($action == 'splitline' && GETPOSTINT('lineid') == $task_time->rowid) {
2782 if (empty($object->id)) {
2783 $idTask = (!empty($id)) ? $id : $task_time->fk_element;
2784 $object->fetch($idTask);
2785 }
2786 $contactsoftask = $object->getListContactId('internal');
2787 if (!in_array($task_time->fk_user, $contactsoftask)) {
2788 $contactsoftask[] = $task_time->fk_user;
2789 }
2790 if (count($contactsoftask) > 0) {
2791 print img_object('', 'user', 'class="hideonsmartphone"');
2792 print $form->select_dolusers($task_time->fk_user, 'userid_line', 0, null, 0, '', $contactsoftask);
2793 } else {
2794 print img_error($langs->trans('FirstAddRessourceToAllocateTime')) . $langs->trans('FirstAddRessourceToAllocateTime');
2795 }
2796 } else {
2797 $userstatic->id = $task_time->fk_user;
2798 $userstatic->lastname = $task_time->lastname;
2799 $userstatic->firstname = $task_time->firstname;
2800 $userstatic->photo = $task_time->photo;
2801 $userstatic->status = $task_time->user_status;
2802 print $userstatic->getNomUrl(-1);
2803 }
2804 print '</td>';
2805 }
2806
2807 // Note
2808 if (!empty($arrayfields['t.note']['checked'])) {
2809 print '<td class="tdoverflowmax300">';
2810 if ($action == 'splitline' && GETPOSTINT('lineid') == $task_time->rowid) {
2811 print '<textarea name="timespent_note_line" class="centpercentimp" rows="' . ROWS_2 . '">' . dol_escape_htmltag($task_time->note, 0, 1) . '</textarea>';
2812 } else {
2813 print dol_nl2br($task_time->note);
2814 }
2815 print '</td>';
2816 } elseif ($action == 'splitline' && GETPOSTINT('lineid') == $task_time->rowid) {
2817 print '<input type="hidden" name="timespent_note_line" rows="' . ROWS_2 . '" value="' . dol_escape_htmltag($task_time->note, 0, 1) . '">';
2818 }
2819
2820 // Last modification
2821 if (!empty($arrayfields['t.tms']['checked'])) {
2822 print '<td class="nowraponall">';
2823 print '</td>';
2824 }
2825
2826 // Time spent - duration
2827 if (!empty($arrayfields['t.element_duration']['checked'])) {
2828 print '<td class="right">';
2829 if ($action == 'splitline' && GETPOSTINT('lineid') == $task_time->rowid) {
2830 print '<input type="hidden" name="old_duration" value="'.$task_time->element_duration.'">';
2831 print $form->select_duration('new_duration', $task_time->element_duration, 0, 'text');
2832 } else {
2833 print convertSecondToTime($task_time->element_duration, 'allhourmin');
2834 }
2835 print '</td>';
2836 }
2837
2838 // Product
2839 if (!empty($arrayfields['t.fk_product']['checked'])) {
2840 print '<td class="nowraponall tdoverflowmax125">';
2841 print '</td>';
2842 }
2843
2844 // Value spent
2845 if (!empty($arrayfields['value']['checked'])) {
2846 print '<td class="right">';
2847 print '<span class="amount">';
2848 $value = price2num($task_time->thm * $task_time->element_duration / 3600, 'MT', 1);
2849 print price($value, 1, $langs, 1, -1, -1, $conf->currency);
2850 print '</span>';
2851 print '</td>';
2852 }
2853
2854 // Value billed
2855 if (!empty($arrayfields['valuebilled']['checked'])) {
2856 print '<td class="right">';
2857 $valuebilled = price2num($task_time->total_ht, '', 1);
2858 if (isset($task_time->total_ht)) {
2859 print price($valuebilled, 1, $langs, 1, -1, -1, $conf->currency);
2860 }
2861 print '</td>';
2862 }
2863
2864 // Extra fields
2865 $obj = $task_time;
2866 $extrafieldsobjectkey = 'projet_task';
2867 $extrafieldsobjectprefix = 'ef.';
2868 include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_print_fields.tpl.php';
2869
2870 // Fields from hook
2871 $parameters = array('arrayfields' => $arrayfields, 'obj' => $task_time, 'mode' => 'split1');
2872 $reshook = $hookmanager->executeHooks('printFieldListValue', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
2873 print $hookmanager->resPrint;
2874
2875 // Action column
2876 if (!$conf->main_checkbox_left_column) {
2877 print '<td class="center nowraponall">';
2878 print '</td>';
2879 }
2880
2881 print "</tr>\n";
2882
2883
2884 // Line for second dispatching
2885
2886 print '<!-- second line --><tr class="oddeven">';
2887
2888 // Action column
2889 if ($conf->main_checkbox_left_column) {
2890 print '<td class="center nowraponall">';
2891 print '</td>';
2892 }
2893
2894 // Date
2895 if (!empty($arrayfields['t.element_date']['checked'])) {
2896 print '<td class="nowraponall">';
2897 if ($action == 'splitline' && GETPOSTINT('lineid') == $task_time->rowid) {
2898 if (empty($task_time->element_date_withhour)) {
2899 print $form->selectDate(($date2 ? $date2 : $date1), 'timeline_2', 3, 3, 2, "timespent_date", 1, 0);
2900 } else {
2901 print $form->selectDate(($date2 ? $date2 : $date1), 'timeline_2', 1, 1, 2, "timespent_date", 1, 0);
2902 }
2903 } else {
2904 print dol_print_date(($date2 ? $date2 : $date1), ($task_time->element_date_withhour ? 'dayhour' : 'day'));
2905 }
2906 print '</td>';
2907 }
2908
2909 // Thirdparty
2910 if (!empty($arrayfields['p.fk_soc']['checked'])) {
2911 print '<td>';
2912 print '</td>';
2913 }
2914
2915 // Thirdparty alias
2916 if (!empty($arrayfields['s.name_alias']['checked'])) {
2917 print '<td>';
2918 print '</td>';
2919 }
2920
2921 // Project ref
2922 if (!empty($allprojectforuser)) {
2923 if ((empty($id) && empty($ref)) || !empty($projectidforalltimes)) { // Not a dedicated task
2924 print '<td>';
2925 print '</td>';
2926 }
2927 }
2928
2929 // Task ref
2930 if (!empty($arrayfields['t.element_ref']['checked'])) {
2931 if ((empty($id) && empty($ref)) || !empty($projectidforalltimes)) { // Not a dedicated task
2932 print '<td class="nowraponall">';
2933 $tasktmp->id = $task_time->fk_element;
2934 $tasktmp->ref = $task_time->ref;
2935 $tasktmp->label = $task_time->label;
2936 print $tasktmp->getNomUrl(1, 'withproject', 'time');
2937 print '</td>';
2938 }
2939 }
2940
2941 // Task label
2942 if (!empty($arrayfields['t.element_label']['checked'])) {
2943 if ((empty($id) && empty($ref)) || !empty($projectidforalltimes)) { // Not a dedicated task
2944 print '<td class="tdoverflowmax125" title="'.dol_escape_htmltag($task_time->label).'">';
2945 print dol_escape_htmltag($task_time->label);
2946 print '</td>';
2947 }
2948 }
2949
2950 // User
2951 if (!empty($arrayfields['author']['checked'])) {
2952 print '<td class="nowraponall tdoverflowmax100">';
2953 if ($action == 'splitline' && GETPOSTINT('lineid') == $task_time->rowid) {
2954 if (empty($object->id)) {
2955 $idTask = (!empty($id)) ? $id : $task_time->fk_element;
2956 $object->fetch($idTask);
2957 }
2958 $contactsoftask = $object->getListContactId('internal');
2959 if (!in_array($task_time->fk_user, $contactsoftask)) {
2960 $contactsoftask[] = $task_time->fk_user;
2961 }
2962 if (count($contactsoftask) > 0) {
2963 print img_object('', 'user', 'class="hideonsmartphone"');
2964 print $form->select_dolusers($task_time->fk_user, 'userid_line_2', 0, null, 0, '', $contactsoftask);
2965 } else {
2966 print img_error($langs->trans('FirstAddRessourceToAllocateTime')) . $langs->trans('FirstAddRessourceToAllocateTime');
2967 }
2968 } else {
2969 $userstatic->id = $task_time->fk_user;
2970 $userstatic->lastname = $task_time->lastname;
2971 $userstatic->firstname = $task_time->firstname;
2972 $userstatic->photo = $task_time->photo;
2973 $userstatic->status = $task_time->user_status;
2974 print $userstatic->getNomUrl(-1);
2975 }
2976 print '</td>';
2977 }
2978
2979 // Note
2980 if (!empty($arrayfields['t.note']['checked'])) {
2981 print '<td class="small tdoverflowmax300"">';
2982 if ($action == 'splitline' && GETPOSTINT('lineid') == $task_time->rowid) {
2983 print '<textarea name="timespent_note_line_2" class="centpercentimp" rows="' . ROWS_2 . '">' . dol_escape_htmltag($task_time->note, 0, 1) . '</textarea>';
2984 } else {
2985 print dol_nl2br($task_time->note);
2986 }
2987 print '</td>';
2988 } elseif ($action == 'splitline' && GETPOSTINT('lineid') == $task_time->rowid) {
2989 print '<input type="hidden" name="timespent_note_line_2" value="' . dol_escape_htmltag($task_time->note, 0, 1) . '">';
2990 }
2991
2992 // Last modification
2993 if (!empty($arrayfields['t.tms']['checked'])) {
2994 print '<td class="nowraponall">';
2995 print dol_print_date($task_time->tms, 'dayhour');
2996 print '</td>';
2997 }
2998
2999 // Time spent - duration
3000 if (!empty($arrayfields['t.element_duration']['checked'])) {
3001 print '<td class="right">';
3002 if ($action == 'splitline' && GETPOSTINT('lineid') == $task_time->rowid) {
3003 print '<input type="hidden" name="old_duration_2" value="0">';
3004 print $form->select_duration('new_duration_2', 0, 0, 'text');
3005 } else {
3006 print convertSecondToTime($task_time->element_duration, 'allhourmin');
3007 }
3008 print '</td>';
3009 }
3010
3011 // Product
3012 if (!empty($arrayfields['t.fk_product']['checked'])) {
3013 print '<td class="nowraponall tdoverflowmax125">';
3014 print '</td>';
3015 }
3016
3017 // Value spent - duration
3018 if (!empty($arrayfields['value']['checked'])) {
3019 print '<td class="right">';
3020 print '<span class="amount nowraponall">';
3021 $value = 0;
3022 print price($value, 1, $langs, 1, -1, -1, $conf->currency);
3023 // Note: On the transverse project view, we also have a warning if value is zero here
3024 print '</span>';
3025 print '</td>';
3026 }
3027
3028 // Value billed
3029 if (!empty($arrayfields['valuebilled']['checked'])) {
3030 print '<td class="right">';
3031 if (isset($task_time->total_ht)) {
3032 $valuebilled = price2num($task_time->total_ht, '', 1);
3033 print '<span class="amount nowraponall small">';
3034 print price($valuebilled, 1, $langs, 1, -1, -1, $conf->currency);
3035 print '</span>';
3036 }
3037 print '</td>';
3038 }
3039
3040 // Extra fields
3041 $obj = $task_time;
3042 $extrafieldsobjectkey = 'projet_task';
3043 $extrafieldsobjectprefix = 'ef.';
3044 include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_print_fields.tpl.php';
3045
3046 // Fields from hook
3047 $parameters = array('arrayfields' => $arrayfields, 'obj' => $task_time, 'mode' => 'split2');
3048 $reshook = $hookmanager->executeHooks('printFieldListValue', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
3049 print $hookmanager->resPrint;
3050
3051 // Action column
3052 if (!$conf->main_checkbox_left_column) {
3053 print '<td class="center nowraponall">';
3054 print '</td>';
3055 }
3056
3057 print "</tr>\n";
3058 }
3059
3060 $i++;
3061 }
3062
3063 // Show total line
3064 //include DOL_DOCUMENT_ROOT.'/core/tpl/list_print_total.tpl.php';
3065 if (isset($totalarray['totaldurationfield']) || isset($totalarray['totalvaluefield'])) {
3066 print '<tr class="liste_total">';
3067 $i = 0;
3068 while ($i < $totalarray['nbfield']) {
3069 $i++;
3070 if ($i == 1) {
3071 if ($num < $limit && empty($offset)) {
3072 print '<td class="left">' . $langs->trans("Total") . '</td>';
3073 } else {
3074 print '<td class="left">'.$form->textwithpicto($langs->trans("Total"), $langs->trans("Totalforthispage")).'</td>';
3075 }
3076 } elseif (isset($totalarray['totaldurationfield']) && $totalarray['totaldurationfield'] == $i) {
3077 print '<td class="right">' . convertSecondToTime($totalarray['totalduration'], 'allhourmin') . '</td>';
3078 } elseif (isset($totalarray['totalvaluefield']) && $totalarray['totalvaluefield'] == $i) {
3079 print '<td class="right">' . price($totalarray['totalvalue']) . '</td>';
3080 //} elseif ($totalarray['totalvaluebilledfield'] == $i) { print '<td class="center">'.price($totalarray['totalvaluebilled']).'</td>';
3081 } else {
3082 print '<td></td>';
3083 }
3084 }
3085 print '</tr>';
3086 }
3087
3088 if (!count($tasks)) {
3089 $totalnboffields = 1;
3090 foreach ($arrayfields as $value) {
3091 if (!empty($value['checked'])) {
3092 $totalnboffields++;
3093 }
3094 }
3095 print '<tr class="oddeven"><td colspan="' . $totalnboffields . '">';
3096 print '<span class="opacitymedium">' . $langs->trans("None") . '</span>';
3097 print '</td></tr>';
3098 }
3099
3100 $parameters = array('arrayfields' => $arrayfields, 'sql' => $sql);
3101 $reshook = $hookmanager->executeHooks('printFieldListFooter', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
3102 print $hookmanager->resPrint;
3103
3104 print "</table>";
3105 print '</div>';
3106 print "</form>";
3107 }
3108}
3109
3110// End of page
3111llxFooter();
3112$db->close();
$id
Support class for third parties, contacts, members, users or resources.
Definition account.php:47
if(! $sortfield) if(! $sortorder) $object
Definition account.php:100
$totalarray
Definition list.php:501
llxFooter($comment='', $zone='private', $disabledoutputofmessages=0)
Empty footer.
Definition wrapper.php:91
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 manage invoices.
Class to manage invoice lines.
Class to manage generation of HTML components Only common components must be here.
Class to manage generation of HTML components for intervention module.
Class to help generate other html components Only common components are here.
Class to manage building of HTML components.
Class to manage products or services.
Class to manage projects.
Class to manage third parties objects (customers, suppliers, prospects...)
Class to manage tasks.
Class for TimeSpent.
Class to manage Dolibarr users.
global $mysoc
dolSqlDateFilter($datefield, $day_date, $month_date, $year_date, $excludefirstand=0, $gm=false)
Generate a SQL string to make a filter into a range (for second of date until last second of date).
Definition date.lib.php:401
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:263
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $conf
The main.inc.php has been included so the following variable are now defined:
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $db
API class for accounts.
$date_start
Variables from include:
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...
get_default_localtax($thirdparty_seller, $thirdparty_buyer, $local, $idprod=0)
Function that return localtax of a product line (according to seller, buyer and product vat rate) If ...
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.
print_barre_liste($title, $page, $file, $options='', $sortfield='', $sortorder='', $morehtmlcenter='', $num=-1, $totalnboflines='', $picto='generic', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limit=-1, $selectlimitsuffix=0, $hidenavigation=0, $pagenavastextinput=0, $morehtmlrightbeforearrow='')
Print a title with navigation controls for pagination.
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)
img_warning($titlealt='default', $moreatt='', $morecss='pictowarning')
Show warning logo.
img_delete($titlealt='default', $other='class="pictodelete"', $morecss='')
Show delete logo.
dolPrintHTML($s, $allowiframe=0, $moreallowedtags=array())
Return a string (that can be on several lines) ready to be output on a HTML page.
dolGetFirstLineOfText($text, $nboflines=1, $charset='UTF-8')
Return first line of text.
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.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dolGetButtonTitle($label, $helpText='', $iconClass='fa fa-file', $url='', $id='', $status=1, $params=array())
Function dolGetButtonTitle : this kind of buttons are used in title in list.
dol_get_fiche_end($notab=0)
Return tab footer of a card.
dol_eval($s, $returnvalue=1, $hideerrors=1, $onlysimplestring='1')
Replace eval function to add more security.
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $allowothertags=array())
Show a picto called object_picto (generic function)
dol_nl2br($stringtoencode, $nl2brmode=0, $forxml=false)
Replace CRLF in string with a HTML BR tag.
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
natural_search($fields, $value, $mode=0, $nofirstand=0, $sqltoadd='')
Generate natural SQL search string for a criteria (this criteria can be tested on one or several fiel...
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
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...
newToken()
Return the value of token currently saved into session with name 'newtoken'.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0, $nodefault=0)
Return value of a param into GET or POST supervariable.
dol_concatdesc($text1, $text2, $forxml=false, $invert=false)
Concat 2 descriptions with a new line between them (second operand after first one with appropriate n...
img_split($titlealt='default', $other='class="pictosplit"')
Show split logo.
GETPOSTINT($paramname, $method=0, $nodefault=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
dol_clone($srcobject, $native=2)
Create a clone of instance of object (new instance with same value for each properties) With native =...
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false, $decorate=0)
Output date in a string format according to outputlangs (or langs if not defined).
img_error($titlealt='default')
Show error logo.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
GETPOSTISSET($paramname)
Return true if we are in a context of submitting the parameter $paramname from a POST of a form.
dol_htmlentitiesbr($stringtoencode, $nl2brmode=0, $pagecodefrom='UTF-8', $removelasteolbr=1)
This function is called to encode a string into a HTML string but differs from htmlentities because a...
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
img_edit($titlealt='default', $float=0, $other='')
Show logo edit/modify fiche.
get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
Function that return vat rate of a product line (according to seller, buyer and product vat rate) VAT...
get_localtax($vatrate, $local, $thirdparty_buyer=null, $thirdparty_seller=null, $vatnpr=0)
Return localtax rate for a particular VAT rate, when selling a product with vat $vatrate,...
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...
print $langs trans("Show") . '< td style="' . $timeColor . '" align="center"> s</td > badge status0 badge status4 badge status3 Error badge status8< td align="center">< span class="badge ' . $badge . '"></span ></td >< td align="center">< a href="#" class="button button-small" onclick="openLogModal(this)" data-req="' . dol_escape_htmltag($reqSafe) . '" data-res="' . dol_escape_htmltag($resSafe) . '" data-err="' . dol_escape_htmltag($errSafe) . '">< span class="fa fa-search-plus"></span ></a ></td ></tr >< tr >< td colspan="' . $colspan . '" class="opacitymedium"></td ></tr ></table ></div ></form > logModal none logModal none s a JSON string
buildzip.php
task_prepare_head($object)
Prepare array with list of tabs.
project_prepare_head(Project $project, $moreparam='')
Prepare array with list of tabs.
restrictedArea(User $user, $features, $object=0, $tableandshare='', $feature2='', $dbt_keyfield='fk_soc', $dbt_select='rowid', $isdraft=0, $nodie=0, $mode='')
Check permissions of a user to show a page and an object.
accessforbidden($message='', $printheader=1, $printfooter=1, $showonlymessage=0, $params=null)
Show a message to say access is forbidden and stop program.