perday.php 36 KB

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