task.class.php 77 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337
  1. <?php
  2. /* Copyright (C) 2008-2014 Laurent Destailleur <eldy@users.sourceforge.net>
  3. * Copyright (C) 2010-2012 Regis Houssin <regis.houssin@inodbox.com>
  4. * Copyright (C) 2014 Marcos García <marcosgdf@gmail.com>
  5. * Copyright (C) 2018 Frédéric France <frederic.france@netlogic.fr>
  6. * Copyright (C) 2020 Juanjo Menent <jmenent@2byte.es>
  7. * Copyright (C) 2022 Charlene Benke <charlene@patas-monkey.com>
  8. *
  9. * This program is free software; you can redistribute it and/or modify
  10. * it under the terms of the GNU General Public License as published by
  11. * the Free Software Foundation; either version 3 of the License, or
  12. * (at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  21. */
  22. /**
  23. * \file htdocs/projet/class/task.class.php
  24. * \ingroup project
  25. * \brief This file is a CRUD class file for Task (Create/Read/Update/Delete)
  26. */
  27. require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
  28. require_once DOL_DOCUMENT_ROOT.'/core/class/commonobjectline.class.php';
  29. require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
  30. /**
  31. * Class to manage tasks
  32. */
  33. class Task extends CommonObjectLine
  34. {
  35. /**
  36. * @var string ID to identify managed object
  37. */
  38. public $element = 'project_task';
  39. /**
  40. * @var string Name of table without prefix where object is stored
  41. */
  42. public $table_element = 'projet_task';
  43. /**
  44. * @var string Field with ID of parent key if this field has a parent
  45. */
  46. public $fk_element = 'fk_task';
  47. /**
  48. * @var string String with name of icon for myobject.
  49. */
  50. public $picto = 'projecttask';
  51. /**
  52. * @var array List of child tables. To test if we can delete object.
  53. */
  54. protected $childtables = array(
  55. 'projet_task_time' => array('name' => 'Task', 'parent' => 'projet_task', 'parentkey' => 'fk_task')
  56. );
  57. /**
  58. * @var int ID parent task
  59. */
  60. public $fk_task_parent = 0;
  61. /**
  62. * @var string Label of task
  63. */
  64. public $label;
  65. /**
  66. * @var string description
  67. */
  68. public $description;
  69. public $duration_effective; // total of time spent on this task
  70. public $planned_workload;
  71. public $date_c;
  72. public $date_start;
  73. public $date_end;
  74. public $progress;
  75. /**
  76. * @deprecated Use date_end instead
  77. */
  78. public $datee;
  79. /**
  80. * @var int ID
  81. */
  82. public $fk_statut;
  83. public $priority;
  84. /**
  85. * @var int ID
  86. */
  87. public $fk_user_creat;
  88. /**
  89. * @var int ID
  90. */
  91. public $fk_user_valid;
  92. public $rang;
  93. public $timespent_min_date;
  94. public $timespent_max_date;
  95. public $timespent_total_duration;
  96. public $timespent_total_amount;
  97. public $timespent_nblinesnull;
  98. public $timespent_nblines;
  99. // For detail of lines of timespent record, there is the property ->lines in common
  100. // Var used to call method addTimeSpent(). Bad practice.
  101. public $timespent_id;
  102. public $timespent_duration;
  103. public $timespent_old_duration;
  104. public $timespent_date;
  105. public $timespent_datehour; // More accurate start date (same than timespent_date but includes hours, minutes and seconds)
  106. public $timespent_withhour; // 1 = we entered also start hours for timesheet line
  107. public $timespent_fk_user;
  108. public $timespent_thm;
  109. public $timespent_note;
  110. public $timespent_fk_product;
  111. public $comments = array();
  112. /**
  113. * @var float budget_amount
  114. */
  115. public $budget_amount;
  116. /**
  117. * @var float project_budget_amount
  118. */
  119. public $project_budget_amount;
  120. public $oldcopy;
  121. /**
  122. * Constructor
  123. *
  124. * @param DoliDB $db Database handler
  125. */
  126. public function __construct($db)
  127. {
  128. $this->db = $db;
  129. }
  130. /**
  131. * Create into database
  132. *
  133. * @param User $user User that create
  134. * @param int $notrigger 0=launch triggers after, 1=disable triggers
  135. * @return int <0 if KO, Id of created object if OK
  136. */
  137. public function create($user, $notrigger = 0)
  138. {
  139. global $conf, $langs;
  140. //For the date
  141. $now = dol_now();
  142. $error = 0;
  143. // Clean parameters
  144. $this->label = trim($this->label);
  145. $this->description = trim($this->description);
  146. if (!empty($this->date_start) && !empty($this->date_end) && $this->date_start > $this->date_end) {
  147. $this->errors[] = $langs->trans('StartDateCannotBeAfterEndDate');
  148. return -1;
  149. }
  150. // Check parameters
  151. // Put here code to add control on parameters values
  152. // Insert request
  153. $sql = "INSERT INTO ".MAIN_DB_PREFIX."projet_task (";
  154. $sql .= "entity";
  155. $sql .= ", fk_projet";
  156. $sql .= ", ref";
  157. $sql .= ", fk_task_parent";
  158. $sql .= ", label";
  159. $sql .= ", description";
  160. $sql .= ", datec";
  161. $sql .= ", fk_user_creat";
  162. $sql .= ", dateo";
  163. $sql .= ", datee";
  164. $sql .= ", planned_workload";
  165. $sql .= ", progress";
  166. $sql .= ", budget_amount";
  167. $sql .= ") VALUES (";
  168. $sql .= (!empty($this->entity) ? (int) $this->entity : (int) $conf->entity);
  169. $sql .= ", ".((int) $this->fk_project);
  170. $sql .= ", ".(!empty($this->ref) ? "'".$this->db->escape($this->ref)."'" : 'null');
  171. $sql .= ", ".((int) $this->fk_task_parent);
  172. $sql .= ", '".$this->db->escape($this->label)."'";
  173. $sql .= ", '".$this->db->escape($this->description)."'";
  174. $sql .= ", '".$this->db->idate($now)."'";
  175. $sql .= ", ".((int) $user->id);
  176. $sql .= ", ".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : 'null');
  177. $sql .= ", ".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : 'null');
  178. $sql .= ", ".(($this->planned_workload != '' && $this->planned_workload >= 0) ? ((int) $this->planned_workload) : 'null');
  179. $sql .= ", ".(($this->progress != '' && $this->progress >= 0) ? ((int) $this->progress) : 'null');
  180. $sql .= ", ".(($this->budget_amount != '' && $this->budget_amount >= 0) ? ((int) $this->budget_amount) : 'null');
  181. $sql .= ")";
  182. $this->db->begin();
  183. dol_syslog(get_class($this)."::create", LOG_DEBUG);
  184. $resql = $this->db->query($sql);
  185. if (!$resql) {
  186. $error++; $this->errors[] = "Error ".$this->db->lasterror();
  187. }
  188. if (!$error) {
  189. $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."projet_task");
  190. if (!$notrigger) {
  191. // Call trigger
  192. $result = $this->call_trigger('TASK_CREATE', $user);
  193. if ($result < 0) {
  194. $error++;
  195. }
  196. // End call triggers
  197. }
  198. }
  199. // Update extrafield
  200. if (!$error) {
  201. if (!$error) {
  202. $result = $this->insertExtraFields();
  203. if ($result < 0) {
  204. $error++;
  205. }
  206. }
  207. }
  208. // Commit or rollback
  209. if ($error) {
  210. foreach ($this->errors as $errmsg) {
  211. dol_syslog(get_class($this)."::create ".$errmsg, LOG_ERR);
  212. $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
  213. }
  214. $this->db->rollback();
  215. return -1 * $error;
  216. } else {
  217. $this->db->commit();
  218. return $this->id;
  219. }
  220. }
  221. /**
  222. * Load object in memory from database
  223. *
  224. * @param int $id Id object
  225. * @param int $ref ref object
  226. * @param int $loadparentdata Also load parent data
  227. * @return int <0 if KO, 0 if not found, >0 if OK
  228. */
  229. public function fetch($id, $ref = '', $loadparentdata = 0)
  230. {
  231. global $langs, $conf;
  232. $sql = "SELECT";
  233. $sql .= " t.rowid,";
  234. $sql .= " t.ref,";
  235. $sql .= " t.entity,";
  236. $sql .= " t.fk_projet as fk_project,";
  237. $sql .= " t.fk_task_parent,";
  238. $sql .= " t.label,";
  239. $sql .= " t.description,";
  240. $sql .= " t.duration_effective,";
  241. $sql .= " t.planned_workload,";
  242. $sql .= " t.datec,";
  243. $sql .= " t.dateo,";
  244. $sql .= " t.datee,";
  245. $sql .= " t.fk_user_creat,";
  246. $sql .= " t.fk_user_valid,";
  247. $sql .= " t.fk_statut,";
  248. $sql .= " t.progress,";
  249. $sql .= " t.budget_amount,";
  250. $sql .= " t.priority,";
  251. $sql .= " t.note_private,";
  252. $sql .= " t.note_public,";
  253. $sql .= " t.rang";
  254. if (!empty($loadparentdata)) {
  255. $sql .= ", t2.ref as task_parent_ref";
  256. $sql .= ", t2.rang as task_parent_position";
  257. }
  258. $sql .= " FROM ".MAIN_DB_PREFIX."projet_task as t";
  259. if (!empty($loadparentdata)) {
  260. $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task as t2 ON t.fk_task_parent = t2.rowid";
  261. }
  262. $sql .= " WHERE ";
  263. if (!empty($ref)) {
  264. $sql .= "entity IN (".getEntity('project').")";
  265. $sql .= " AND t.ref = '".$this->db->escape($ref)."'";
  266. } else {
  267. $sql .= "t.rowid = ".((int) $id);
  268. }
  269. dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
  270. $resql = $this->db->query($sql);
  271. if ($resql) {
  272. $num_rows = $this->db->num_rows($resql);
  273. if ($num_rows) {
  274. $obj = $this->db->fetch_object($resql);
  275. $this->id = $obj->rowid;
  276. $this->ref = $obj->ref;
  277. $this->entity = $obj->entity;
  278. $this->fk_project = $obj->fk_project;
  279. $this->fk_task_parent = $obj->fk_task_parent;
  280. $this->label = $obj->label;
  281. $this->description = $obj->description;
  282. $this->duration_effective = $obj->duration_effective;
  283. $this->planned_workload = $obj->planned_workload;
  284. $this->date_c = $this->db->jdate($obj->datec);
  285. $this->date_start = $this->db->jdate($obj->dateo);
  286. $this->date_end = $this->db->jdate($obj->datee);
  287. $this->fk_user_creat = $obj->fk_user_creat;
  288. $this->fk_user_valid = $obj->fk_user_valid;
  289. $this->fk_statut = $obj->fk_statut;
  290. $this->progress = $obj->progress;
  291. $this->budget_amount = $obj->budget_amount;
  292. $this->priority = $obj->priority;
  293. $this->note_private = $obj->note_private;
  294. $this->note_public = $obj->note_public;
  295. $this->rang = $obj->rang;
  296. if (!empty($loadparentdata)) {
  297. $this->task_parent_ref = $obj->task_parent_ref;
  298. $this->task_parent_position = $obj->task_parent_position;
  299. }
  300. // Retrieve all extrafield
  301. $this->fetch_optionals();
  302. }
  303. $this->db->free($resql);
  304. if ($num_rows) {
  305. return 1;
  306. } else {
  307. return 0;
  308. }
  309. } else {
  310. $this->error = "Error ".$this->db->lasterror();
  311. return -1;
  312. }
  313. }
  314. /**
  315. * Update database
  316. *
  317. * @param User $user User that modify
  318. * @param int $notrigger 0=launch triggers after, 1=disable triggers
  319. * @return int <=0 if KO, >0 if OK
  320. */
  321. public function update($user = null, $notrigger = 0)
  322. {
  323. global $conf, $langs;
  324. $error = 0;
  325. // Clean parameters
  326. if (isset($this->fk_project)) {
  327. $this->fk_project = trim($this->fk_project);
  328. }
  329. if (isset($this->ref)) {
  330. $this->ref = trim($this->ref);
  331. }
  332. if (isset($this->fk_task_parent)) {
  333. $this->fk_task_parent = (int) $this->fk_task_parent;
  334. }
  335. if (isset($this->label)) {
  336. $this->label = trim($this->label);
  337. }
  338. if (isset($this->description)) {
  339. $this->description = trim($this->description);
  340. }
  341. if (isset($this->duration_effective)) {
  342. $this->duration_effective = trim($this->duration_effective);
  343. }
  344. if (isset($this->planned_workload)) {
  345. $this->planned_workload = trim($this->planned_workload);
  346. }
  347. if (isset($this->budget_amount)) {
  348. $this->budget_amount = trim($this->budget_amount);
  349. }
  350. if (!empty($this->date_start) && !empty($this->date_end) && $this->date_start > $this->date_end) {
  351. $this->errors[] = $langs->trans('StartDateCannotBeAfterEndDate');
  352. return -1;
  353. }
  354. // Check parameters
  355. // Put here code to add control on parameters values
  356. // Update request
  357. $sql = "UPDATE ".MAIN_DB_PREFIX."projet_task SET";
  358. $sql .= " fk_projet=".(isset($this->fk_project) ? $this->fk_project : "null").",";
  359. $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "'".$this->db->escape($this->id)."'").",";
  360. $sql .= " fk_task_parent=".(isset($this->fk_task_parent) ? $this->fk_task_parent : "null").",";
  361. $sql .= " label=".(isset($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
  362. $sql .= " description=".(isset($this->description) ? "'".$this->db->escape($this->description)."'" : "null").",";
  363. $sql .= " duration_effective=".(isset($this->duration_effective) ? $this->duration_effective : "null").",";
  364. $sql .= " planned_workload=".((isset($this->planned_workload) && $this->planned_workload != '') ? $this->planned_workload : "null").",";
  365. $sql .= " dateo=".($this->date_start != '' ? "'".$this->db->idate($this->date_start)."'" : 'null').",";
  366. $sql .= " datee=".($this->date_end != '' ? "'".$this->db->idate($this->date_end)."'" : 'null').",";
  367. $sql .= " progress=".(($this->progress != '' && $this->progress >= 0) ? $this->progress : 'null').",";
  368. $sql .= " budget_amount=".(($this->budget_amount != '' && $this->budget_amount >= 0) ? $this->budget_amount : 'null').",";
  369. $sql .= " rang=".((!empty($this->rang)) ? $this->rang : "0");
  370. $sql .= " WHERE rowid=".((int) $this->id);
  371. $this->db->begin();
  372. dol_syslog(get_class($this)."::update", LOG_DEBUG);
  373. $resql = $this->db->query($sql);
  374. if (!$resql) {
  375. $error++; $this->errors[] = "Error ".$this->db->lasterror();
  376. }
  377. // Update extrafield
  378. if (!$error) {
  379. $result = $this->insertExtraFields();
  380. if ($result < 0) {
  381. $error++;
  382. }
  383. }
  384. if (!$error && !empty($conf->global->PROJECT_CLASSIFY_CLOSED_WHEN_ALL_TASKS_DONE)) {
  385. // Close the parent project if it is open (validated) and its tasks are 100% completed
  386. $project = new Project($this->db);
  387. if ($project->fetch($this->fk_project) > 0) {
  388. if ($project->statut == Project::STATUS_VALIDATED) {
  389. $project->getLinesArray(null); // this method does not return <= 0 if fails
  390. $projectCompleted = array_reduce(
  391. $project->lines,
  392. function ($allTasksCompleted, $task) {
  393. return $allTasksCompleted && $task->progress >= 100;
  394. },
  395. 1
  396. );
  397. if ($projectCompleted) {
  398. if ($project->setClose($user) <= 0) {
  399. $error++;
  400. }
  401. }
  402. }
  403. } else {
  404. $error++;
  405. }
  406. if ($error) {
  407. $this->errors[] = $project->error;
  408. }
  409. }
  410. if (!$error) {
  411. if (!$notrigger) {
  412. // Call trigger
  413. $result = $this->call_trigger('TASK_MODIFY', $user);
  414. if ($result < 0) {
  415. $error++;
  416. }
  417. // End call triggers
  418. }
  419. }
  420. if (!$error && (is_object($this->oldcopy) && $this->oldcopy->ref !== $this->ref)) {
  421. // We remove directory
  422. if ($conf->project->dir_output) {
  423. $project = new Project($this->db);
  424. $project->fetch($this->fk_project);
  425. $olddir = $conf->project->dir_output.'/'.dol_sanitizeFileName($project->ref).'/'.dol_sanitizeFileName($this->oldcopy->ref);
  426. $newdir = $conf->project->dir_output.'/'.dol_sanitizeFileName($project->ref).'/'.dol_sanitizeFileName($this->ref);
  427. if (file_exists($olddir)) {
  428. include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
  429. $res = dol_move_dir($olddir, $newdir);
  430. if (!$res) {
  431. $langs->load("errors");
  432. $this->error = $langs->trans('ErrorFailToRenameDir', $olddir, $newdir);
  433. $error++;
  434. }
  435. }
  436. }
  437. }
  438. // Commit or rollback
  439. if ($error) {
  440. foreach ($this->errors as $errmsg) {
  441. dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
  442. $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
  443. }
  444. $this->db->rollback();
  445. return -1 * $error;
  446. } else {
  447. $this->db->commit();
  448. return 1;
  449. }
  450. }
  451. /**
  452. * Delete task from database
  453. *
  454. * @param User $user User that delete
  455. * @param int $notrigger 0=launch triggers after, 1=disable triggers
  456. * @return int <0 if KO, >0 if OK
  457. */
  458. public function delete($user, $notrigger = 0)
  459. {
  460. global $conf, $langs;
  461. require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
  462. $error = 0;
  463. $this->db->begin();
  464. if ($this->hasChildren() > 0) {
  465. dol_syslog(get_class($this)."::delete Can't delete record as it has some sub tasks", LOG_WARNING);
  466. $this->error = 'ErrorRecordHasSubTasks';
  467. $this->db->rollback();
  468. return 0;
  469. }
  470. $objectisused = $this->isObjectUsed($this->id);
  471. if (!empty($objectisused)) {
  472. dol_syslog(get_class($this)."::delete Can't delete record as it has some child", LOG_WARNING);
  473. $this->error = 'ErrorRecordHasChildren';
  474. $this->db->rollback();
  475. return 0;
  476. }
  477. if (!$error) {
  478. // Delete linked contacts
  479. $res = $this->delete_linked_contact();
  480. if ($res < 0) {
  481. $this->error = 'ErrorFailToDeleteLinkedContact';
  482. //$error++;
  483. $this->db->rollback();
  484. return 0;
  485. }
  486. }
  487. if (!$error) {
  488. $sql = "DELETE FROM ".MAIN_DB_PREFIX."projet_task_time";
  489. $sql .= " WHERE fk_task = ".((int) $this->id);
  490. $resql = $this->db->query($sql);
  491. if (!$resql) {
  492. $error++; $this->errors[] = "Error ".$this->db->lasterror();
  493. }
  494. }
  495. if (!$error) {
  496. $sql = "DELETE FROM ".MAIN_DB_PREFIX."projet_task_extrafields";
  497. $sql .= " WHERE fk_object = ".((int) $this->id);
  498. $resql = $this->db->query($sql);
  499. if (!$resql) {
  500. $error++; $this->errors[] = "Error ".$this->db->lasterror();
  501. }
  502. }
  503. if (!$error) {
  504. $sql = "DELETE FROM ".MAIN_DB_PREFIX."projet_task";
  505. $sql .= " WHERE rowid=".((int) $this->id);
  506. $resql = $this->db->query($sql);
  507. if (!$resql) {
  508. $error++; $this->errors[] = "Error ".$this->db->lasterror();
  509. }
  510. }
  511. if (!$error) {
  512. if (!$notrigger) {
  513. // Call trigger
  514. $result = $this->call_trigger('TASK_DELETE', $user);
  515. if ($result < 0) {
  516. $error++;
  517. }
  518. // End call triggers
  519. }
  520. }
  521. // Commit or rollback
  522. if ($error) {
  523. foreach ($this->errors as $errmsg) {
  524. dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
  525. $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
  526. }
  527. $this->db->rollback();
  528. return -1 * $error;
  529. } else {
  530. //Delete associated link file
  531. if ($conf->project->dir_output) {
  532. $projectstatic = new Project($this->db);
  533. $projectstatic->fetch($this->fk_project);
  534. $dir = $conf->project->dir_output."/".dol_sanitizeFileName($projectstatic->ref).'/'.dol_sanitizeFileName($this->id);
  535. dol_syslog(get_class($this)."::delete dir=".$dir, LOG_DEBUG);
  536. if (file_exists($dir)) {
  537. require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
  538. $res = @dol_delete_dir_recursive($dir);
  539. if (!$res) {
  540. $this->error = 'ErrorFailToDeleteDir';
  541. $this->db->rollback();
  542. return 0;
  543. }
  544. }
  545. }
  546. $this->db->commit();
  547. return 1;
  548. }
  549. }
  550. /**
  551. * Return nb of children
  552. *
  553. * @return int <0 if KO, 0 if no children, >0 if OK
  554. */
  555. public function hasChildren()
  556. {
  557. $error = 0;
  558. $ret = 0;
  559. $sql = "SELECT COUNT(*) as nb";
  560. $sql .= " FROM ".MAIN_DB_PREFIX."projet_task";
  561. $sql .= " WHERE fk_task_parent = ".((int) $this->id);
  562. dol_syslog(get_class($this)."::hasChildren", LOG_DEBUG);
  563. $resql = $this->db->query($sql);
  564. if (!$resql) {
  565. $error++; $this->errors[] = "Error ".$this->db->lasterror();
  566. } else {
  567. $obj = $this->db->fetch_object($resql);
  568. if ($obj) {
  569. $ret = $obj->nb;
  570. }
  571. $this->db->free($resql);
  572. }
  573. if (!$error) {
  574. return $ret;
  575. } else {
  576. return -1;
  577. }
  578. }
  579. /**
  580. * Return nb of time spent
  581. *
  582. * @return int <0 if KO, 0 if no children, >0 if OK
  583. */
  584. public function hasTimeSpent()
  585. {
  586. $error = 0;
  587. $ret = 0;
  588. $sql = "SELECT COUNT(*) as nb";
  589. $sql .= " FROM ".MAIN_DB_PREFIX."projet_task_time";
  590. $sql .= " WHERE fk_task = ".((int) $this->id);
  591. dol_syslog(get_class($this)."::hasTimeSpent", LOG_DEBUG);
  592. $resql = $this->db->query($sql);
  593. if (!$resql) {
  594. $error++; $this->errors[] = "Error ".$this->db->lasterror();
  595. } else {
  596. $obj = $this->db->fetch_object($resql);
  597. if ($obj) {
  598. $ret = $obj->nb;
  599. }
  600. $this->db->free($resql);
  601. }
  602. if (!$error) {
  603. return $ret;
  604. } else {
  605. return -1;
  606. }
  607. }
  608. /**
  609. * Return clicable name (with picto eventually)
  610. *
  611. * @param int $withpicto 0=No picto, 1=Include picto into link, 2=Only picto
  612. * @param string $option 'withproject' or ''
  613. * @param string $mode Mode 'task', 'time', 'contact', 'note', document' define page to link to.
  614. * @param int $addlabel 0=Default, 1=Add label into string, >1=Add first chars into string
  615. * @param string $sep Separator between ref and label if option addlabel is set
  616. * @param int $notooltip 1=Disable tooltip
  617. * @param int $save_lastsearch_value -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking
  618. * @return string Chaine avec URL
  619. */
  620. public function getNomUrl($withpicto = 0, $option = '', $mode = 'task', $addlabel = 0, $sep = ' - ', $notooltip = 0, $save_lastsearch_value = -1)
  621. {
  622. global $conf, $langs, $user;
  623. if (!empty($conf->dol_no_mouse_hover)) {
  624. $notooltip = 1; // Force disable tooltips
  625. }
  626. $result = '';
  627. $label = img_picto('', $this->picto).' <u>'.$langs->trans("Task").'</u>';
  628. if (!empty($this->ref)) {
  629. $label .= '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
  630. }
  631. if (!empty($this->label)) {
  632. $label .= '<br><b>'.$langs->trans('LabelTask').':</b> '.$this->label;
  633. }
  634. if ($this->date_start || $this->date_end) {
  635. $label .= "<br>".get_date_range($this->date_start, $this->date_end, '', $langs, 0);
  636. }
  637. $url = DOL_URL_ROOT.'/projet/tasks/'.$mode.'.php?id='.$this->id.($option == 'withproject' ? '&withproject=1' : '');
  638. // Add param to save lastsearch_values or not
  639. $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
  640. if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
  641. $add_save_lastsearch_values = 1;
  642. }
  643. if ($add_save_lastsearch_values) {
  644. $url .= '&save_lastsearch_values=1';
  645. }
  646. $linkclose = '';
  647. if (empty($notooltip)) {
  648. if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
  649. $label = $langs->trans("ShowTask");
  650. $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
  651. }
  652. $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';
  653. $linkclose .= ' class="classfortooltip nowraponall"';
  654. } else {
  655. $linkclose .= ' class="nowraponall"';
  656. }
  657. $linkstart = '<a href="'.$url.'"';
  658. $linkstart .= $linkclose.'>';
  659. $linkend = '</a>';
  660. $picto = 'projecttask';
  661. $result .= $linkstart;
  662. if ($withpicto) {
  663. $result .= img_object(($notooltip ? '' : $label), $picto, ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip ? 0 : 1);
  664. }
  665. if ($withpicto != 2) {
  666. $result .= $this->ref;
  667. }
  668. $result .= $linkend;
  669. if ($withpicto != 2) {
  670. $result .= (($addlabel && $this->label) ? $sep.dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
  671. }
  672. return $result;
  673. }
  674. /**
  675. * Initialise an instance with random values.
  676. * Used to build previews or test instances.
  677. * id must be 0 if object instance is a specimen.
  678. *
  679. * @return void
  680. */
  681. public function initAsSpecimen()
  682. {
  683. $this->id = 0;
  684. $this->fk_project = '';
  685. $this->ref = 'TK01';
  686. $this->fk_task_parent = null;
  687. $this->label = 'Specimen task TK01';
  688. $this->duration_effective = '';
  689. $this->fk_user_creat = null;
  690. $this->progress = '25';
  691. $this->fk_statut = null;
  692. $this->note = 'This is a specimen task not';
  693. }
  694. /**
  695. * Return list of tasks for all projects or for one particular project
  696. * Sort order is on project, then on position of task, and last on start date of first level task
  697. *
  698. * @param User $usert Object user to limit tasks affected to a particular user
  699. * @param User $userp Object user to limit projects of a particular user and public projects
  700. * @param int $projectid Project id
  701. * @param int $socid Third party id
  702. * @param int $mode 0=Return list of tasks and their projects, 1=Return projects and tasks if exists
  703. * @param string $filteronproj Filter on project ref or label
  704. * @param string $filteronprojstatus Filter on project status ('-1'=no filter, '0,1'=Draft+Validated only)
  705. * @param string $morewherefilter Add more filter into where SQL request (must start with ' AND ...')
  706. * @param string $filteronprojuser Filter on user that is a contact of project
  707. * @param string $filterontaskuser Filter on user assigned to task
  708. * @param array $extrafields Show additional column from project or task
  709. * @param int $includebilltime Calculate also the time to bill and billed
  710. * @param array $search_array_options Array of search
  711. * @param int $loadextras Fetch all Extrafields on each task
  712. * @param int $loadRoleMode 1= will test Roles on task; 0 used in delete project action
  713. * @param string $sortfield Sort field
  714. * @param string $sortorder Sort order
  715. * @return array Array of tasks
  716. */
  717. public function getTasksArray($usert = null, $userp = null, $projectid = 0, $socid = 0, $mode = 0, $filteronproj = '', $filteronprojstatus = '-1', $morewherefilter = '', $filteronprojuser = 0, $filterontaskuser = 0, $extrafields = array(), $includebilltime = 0, $search_array_options = array(), $loadextras = 0, $loadRoleMode = 1, $sortfield = '', $sortorder = '')
  718. {
  719. global $conf, $hookmanager;
  720. $tasks = array();
  721. //print $usert.'-'.$userp.'-'.$projectid.'-'.$socid.'-'.$mode.'<br>';
  722. // List of tasks (does not care about permissions. Filtering will be done later)
  723. $sql = "SELECT ";
  724. if ($filteronprojuser > 0 || $filterontaskuser > 0) {
  725. $sql .= " DISTINCT"; // We may get several time the same record if user has several roles on same project/task
  726. }
  727. $sql .= " p.rowid as projectid, p.ref, p.title as plabel, p.public, p.fk_statut as projectstatus, p.usage_bill_time,";
  728. $sql .= " t.rowid as taskid, t.ref as taskref, t.label, t.description, t.fk_task_parent, t.duration_effective, t.progress, t.fk_statut as status,";
  729. $sql .= " t.dateo as date_start, t.datee as date_end, t.planned_workload, t.rang,";
  730. $sql .= " t.description, ";
  731. $sql .= " t.budget_amount, ";
  732. $sql .= " s.rowid as thirdparty_id, s.nom as thirdparty_name, s.email as thirdparty_email,";
  733. $sql .= " p.fk_opp_status, p.opp_amount, p.opp_percent, p.budget_amount as project_budget_amount";
  734. if (!empty($extrafields->attributes['projet']['label'])) {
  735. foreach ($extrafields->attributes['projet']['label'] as $key => $val) {
  736. $sql .= ($extrafields->attributes['projet']['type'][$key] != 'separate' ? ",efp.".$key." as options_".$key : '');
  737. }
  738. }
  739. if (!empty($extrafields->attributes['projet_task']['label'])) {
  740. foreach ($extrafields->attributes['projet_task']['label'] as $key => $val) {
  741. $sql .= ($extrafields->attributes['projet_task']['type'][$key] != 'separate' ? ",efpt.".$key." as options_".$key : '');
  742. }
  743. }
  744. if ($includebilltime) {
  745. $sql .= ", SUM(tt.task_duration * ".$this->db->ifsql("invoice_id IS NULL", "1", "0").") as tobill, SUM(tt.task_duration * ".$this->db->ifsql("invoice_id IS NULL", "0", "1").") as billed";
  746. }
  747. $sql .= " FROM ".MAIN_DB_PREFIX."projet as p";
  748. $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
  749. $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_extrafields as efp ON (p.rowid = efp.fk_object)";
  750. if ($mode == 0) {
  751. if ($filteronprojuser > 0) {
  752. $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec";
  753. $sql .= ", ".MAIN_DB_PREFIX."c_type_contact as ctc";
  754. }
  755. $sql .= ", ".MAIN_DB_PREFIX."projet_task as t";
  756. if ($includebilltime) {
  757. $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task_time as tt ON tt.fk_task = t.rowid";
  758. }
  759. if ($filterontaskuser > 0) {
  760. $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec2";
  761. $sql .= ", ".MAIN_DB_PREFIX."c_type_contact as ctc2";
  762. }
  763. $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task_extrafields as efpt ON (t.rowid = efpt.fk_object)";
  764. $sql .= " WHERE p.entity IN (".getEntity('project').")";
  765. $sql .= " AND t.fk_projet = p.rowid";
  766. } elseif ($mode == 1) {
  767. if ($filteronprojuser > 0) {
  768. $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec";
  769. $sql .= ", ".MAIN_DB_PREFIX."c_type_contact as ctc";
  770. }
  771. if ($filterontaskuser > 0) {
  772. $sql .= ", ".MAIN_DB_PREFIX."projet_task as t";
  773. if ($includebilltime) {
  774. $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task_time as tt ON tt.fk_task = t.rowid";
  775. }
  776. $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec2";
  777. $sql .= ", ".MAIN_DB_PREFIX."c_type_contact as ctc2";
  778. } else {
  779. $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task as t on t.fk_projet = p.rowid";
  780. if ($includebilltime) {
  781. $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task_time as tt ON tt.fk_task = t.rowid";
  782. }
  783. }
  784. $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task_extrafields as efpt ON (t.rowid = efpt.fk_object)";
  785. $sql .= " WHERE p.entity IN (".getEntity('project').")";
  786. } else {
  787. return 'BadValueForParameterMode';
  788. }
  789. if ($filteronprojuser > 0) {
  790. $sql .= " AND p.rowid = ec.element_id";
  791. $sql .= " AND ctc.rowid = ec.fk_c_type_contact";
  792. $sql .= " AND ctc.element = 'project'";
  793. $sql .= " AND ec.fk_socpeople = ".((int) $filteronprojuser);
  794. $sql .= " AND ec.statut = 4";
  795. $sql .= " AND ctc.source = 'internal'";
  796. }
  797. if ($filterontaskuser > 0) {
  798. $sql .= " AND t.fk_projet = p.rowid";
  799. $sql .= " AND p.rowid = ec2.element_id";
  800. $sql .= " AND ctc2.rowid = ec2.fk_c_type_contact";
  801. $sql .= " AND ctc2.element = 'project_task'";
  802. $sql .= " AND ec2.fk_socpeople = ".((int) $filterontaskuser);
  803. $sql .= " AND ec2.statut = 4";
  804. $sql .= " AND ctc2.source = 'internal'";
  805. }
  806. if ($socid) {
  807. $sql .= " AND p.fk_soc = ".((int) $socid);
  808. }
  809. if ($projectid) {
  810. $sql .= " AND p.rowid IN (".$this->db->sanitize($projectid).")";
  811. }
  812. if ($filteronproj) {
  813. $sql .= natural_search(array("p.ref", "p.title"), $filteronproj);
  814. }
  815. if ($filteronprojstatus && $filteronprojstatus != '-1') {
  816. $sql .= " AND p.fk_statut IN (".$this->db->sanitize($filteronprojstatus).")";
  817. }
  818. if ($morewherefilter) {
  819. $sql .= $morewherefilter;
  820. }
  821. // Add where from extra fields
  822. $extrafieldsobjectkey = 'projet_task';
  823. $extrafieldsobjectprefix = 'efpt.';
  824. global $db; // needed for extrafields_list_search_sql.tpl
  825. include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_sql.tpl.php';
  826. // Add where from hooks
  827. $parameters = array();
  828. $reshook = $hookmanager->executeHooks('printFieldListWhere', $parameters); // Note that $action and $object may have been modified by hook
  829. $sql .= $hookmanager->resPrint;
  830. if ($includebilltime) {
  831. $sql .= " GROUP BY p.rowid, p.ref, p.title, p.public, p.fk_statut, p.usage_bill_time,";
  832. $sql .= " t.datec, t.dateo, t.datee, t.tms,";
  833. $sql .= " t.rowid, t.ref, t.label, t.description, t.fk_task_parent, t.duration_effective, t.progress, t.fk_statut,";
  834. $sql .= " t.dateo, t.datee, t.planned_workload, t.rang,";
  835. $sql .= " t.description, ";
  836. $sql .= " t.budget_amount, ";
  837. $sql .= " s.rowid, s.nom, s.email,";
  838. $sql .= " p.fk_opp_status, p.opp_amount, p.opp_percent, p.budget_amount";
  839. if (!empty($extrafields->attributes['projet']['label'])) {
  840. foreach ($extrafields->attributes['projet']['label'] as $key => $val) {
  841. $sql .= ($extrafields->attributes['projet']['type'][$key] != 'separate' ? ",efp.".$key : '');
  842. }
  843. }
  844. if (!empty($extrafields->attributes['projet_task']['label'])) {
  845. foreach ($extrafields->attributes['projet_task']['label'] as $key => $val) {
  846. $sql .= ($extrafields->attributes['projet_task']['type'][$key] != 'separate' ? ",efpt.".$key : '');
  847. }
  848. }
  849. }
  850. if ($sortfield && $sortorder) {
  851. $sql .= $this->db->order($sortfield, $sortorder);
  852. } else {
  853. $sql .= " ORDER BY p.ref, t.rang, t.dateo";
  854. }
  855. //print $sql;exit;
  856. dol_syslog(get_class($this)."::getTasksArray", LOG_DEBUG);
  857. $resql = $this->db->query($sql);
  858. if ($resql) {
  859. $num = $this->db->num_rows($resql);
  860. $i = 0;
  861. // Loop on each record found, so each couple (project id, task id)
  862. while ($i < $num) {
  863. $error = 0;
  864. $obj = $this->db->fetch_object($resql);
  865. if ($loadRoleMode) {
  866. if ((!$obj->public) && (is_object($userp))) { // If not public project and we ask a filter on project owned by a user
  867. if (!$this->getUserRolesForProjectsOrTasks($userp, 0, $obj->projectid, 0)) {
  868. $error++;
  869. }
  870. }
  871. if (is_object($usert)) { // If we ask a filter on a user affected to a task
  872. if (!$this->getUserRolesForProjectsOrTasks(0, $usert, $obj->projectid, $obj->taskid)) {
  873. $error++;
  874. }
  875. }
  876. }
  877. if (!$error) {
  878. $tasks[$i] = new Task($this->db);
  879. $tasks[$i]->id = $obj->taskid;
  880. $tasks[$i]->ref = $obj->taskref;
  881. $tasks[$i]->fk_project = $obj->projectid;
  882. $tasks[$i]->projectref = $obj->ref;
  883. $tasks[$i]->projectlabel = $obj->plabel;
  884. $tasks[$i]->projectstatus = $obj->projectstatus;
  885. $tasks[$i]->fk_opp_status = $obj->fk_opp_status;
  886. $tasks[$i]->opp_amount = $obj->opp_amount;
  887. $tasks[$i]->opp_percent = $obj->opp_percent;
  888. $tasks[$i]->budget_amount = $obj->budget_amount;
  889. $tasks[$i]->project_budget_amount = $obj->project_budget_amount;
  890. $tasks[$i]->usage_bill_time = $obj->usage_bill_time;
  891. $tasks[$i]->label = $obj->label;
  892. $tasks[$i]->description = $obj->description;
  893. $tasks[$i]->fk_parent = $obj->fk_task_parent; // deprecated
  894. $tasks[$i]->fk_task_parent = $obj->fk_task_parent;
  895. $tasks[$i]->duration = $obj->duration_effective;
  896. $tasks[$i]->planned_workload = $obj->planned_workload;
  897. if ($includebilltime) {
  898. $tasks[$i]->tobill = $obj->tobill;
  899. $tasks[$i]->billed = $obj->billed;
  900. }
  901. $tasks[$i]->progress = $obj->progress;
  902. $tasks[$i]->fk_statut = $obj->status;
  903. $tasks[$i]->public = $obj->public;
  904. $tasks[$i]->date_start = $this->db->jdate($obj->date_start);
  905. $tasks[$i]->date_end = $this->db->jdate($obj->date_end);
  906. $tasks[$i]->rang = $obj->rang;
  907. $tasks[$i]->socid = $obj->thirdparty_id; // For backward compatibility
  908. $tasks[$i]->thirdparty_id = $obj->thirdparty_id;
  909. $tasks[$i]->thirdparty_name = $obj->thirdparty_name;
  910. $tasks[$i]->thirdparty_email = $obj->thirdparty_email;
  911. if (!empty($extrafields->attributes['projet']['label'])) {
  912. foreach ($extrafields->attributes['projet']['label'] as $key => $val) {
  913. if ($extrafields->attributes['projet']['type'][$key] != 'separate') {
  914. $tasks[$i]->{'options_'.$key} = $obj->{'options_'.$key};
  915. }
  916. }
  917. }
  918. if (!empty($extrafields->attributes['projet_task']['label'])) {
  919. foreach ($extrafields->attributes['projet_task']['label'] as $key => $val) {
  920. if ($extrafields->attributes['projet_task']['type'][$key] != 'separate') {
  921. $tasks[$i]->{'options_'.$key} = $obj->{'options_'.$key};
  922. }
  923. }
  924. }
  925. if ($loadextras) {
  926. $tasks[$i]->fetch_optionals();
  927. }
  928. }
  929. $i++;
  930. }
  931. $this->db->free($resql);
  932. } else {
  933. dol_print_error($this->db);
  934. }
  935. return $tasks;
  936. }
  937. /**
  938. * Return list of roles for a user for each projects or each tasks (or a particular project or a particular task).
  939. *
  940. * @param User $userp Return roles on project for this internal user. If set, usert and taskid must not be defined.
  941. * @param User $usert Return roles on task for this internal user. If set userp must NOT be defined. -1 means no filter.
  942. * @param int $projectid Project id list separated with , to filter on project
  943. * @param int $taskid Task id to filter on a task
  944. * @param integer $filteronprojstatus Filter on project status if userp is set. Not used if userp not defined.
  945. * @return array Array (projectid => 'list of roles for project' or taskid => 'list of roles for task')
  946. */
  947. public function getUserRolesForProjectsOrTasks($userp, $usert, $projectid = '', $taskid = 0, $filteronprojstatus = -1)
  948. {
  949. $arrayroles = array();
  950. dol_syslog(get_class($this)."::getUserRolesForProjectsOrTasks userp=".is_object($userp)." usert=".is_object($usert)." projectid=".$projectid." taskid=".$taskid);
  951. // We want role of user for a projet or role of user for a task. Both are not possible.
  952. if (empty($userp) && empty($usert)) {
  953. $this->error = "CallWithWrongParameters";
  954. return -1;
  955. }
  956. if (!empty($userp) && !empty($usert)) {
  957. $this->error = "CallWithWrongParameters";
  958. return -1;
  959. }
  960. /* Liste des taches et role sur les projets ou taches */
  961. $sql = "SELECT pt.rowid as pid, ec.element_id, ctc.code, ctc.source";
  962. if ($userp) {
  963. $sql .= " FROM ".MAIN_DB_PREFIX."projet as pt";
  964. }
  965. if ($usert && $filteronprojstatus > -1) {
  966. $sql .= " FROM ".MAIN_DB_PREFIX."projet as p, ".MAIN_DB_PREFIX."projet_task as pt";
  967. }
  968. if ($usert && $filteronprojstatus <= -1) {
  969. $sql .= " FROM ".MAIN_DB_PREFIX."projet_task as pt";
  970. }
  971. $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec";
  972. $sql .= ", ".MAIN_DB_PREFIX."c_type_contact as ctc";
  973. $sql .= " WHERE pt.rowid = ec.element_id";
  974. if ($userp && $filteronprojstatus > -1) {
  975. $sql .= " AND pt.fk_statut = ".((int) $filteronprojstatus);
  976. }
  977. if ($usert && $filteronprojstatus > -1) {
  978. $sql .= " AND pt.fk_projet = p.rowid AND p.fk_statut = ".((int) $filteronprojstatus);
  979. }
  980. if ($userp) {
  981. $sql .= " AND ctc.element = 'project'";
  982. }
  983. if ($usert) {
  984. $sql .= " AND ctc.element = 'project_task'";
  985. }
  986. $sql .= " AND ctc.rowid = ec.fk_c_type_contact";
  987. if ($userp) {
  988. $sql .= " AND ec.fk_socpeople = ".((int) $userp->id);
  989. }
  990. if ($usert) {
  991. $sql .= " AND ec.fk_socpeople = ".((int) $usert->id);
  992. }
  993. $sql .= " AND ec.statut = 4";
  994. $sql .= " AND ctc.source = 'internal'";
  995. if ($projectid) {
  996. if ($userp) {
  997. $sql .= " AND pt.rowid IN (".$this->db->sanitize($projectid).")";
  998. }
  999. if ($usert) {
  1000. $sql .= " AND pt.fk_projet IN (".$this->db->sanitize($projectid).")";
  1001. }
  1002. }
  1003. if ($taskid) {
  1004. if ($userp) {
  1005. $sql .= " ERROR SHOULD NOT HAPPENS";
  1006. }
  1007. if ($usert) {
  1008. $sql .= " AND pt.rowid = ".((int) $taskid);
  1009. }
  1010. }
  1011. //print $sql;
  1012. dol_syslog(get_class($this)."::getUserRolesForProjectsOrTasks execute request", LOG_DEBUG);
  1013. $resql = $this->db->query($sql);
  1014. if ($resql) {
  1015. $num = $this->db->num_rows($resql);
  1016. $i = 0;
  1017. while ($i < $num) {
  1018. $obj = $this->db->fetch_object($resql);
  1019. if (empty($arrayroles[$obj->pid])) {
  1020. $arrayroles[$obj->pid] = $obj->code;
  1021. } else {
  1022. $arrayroles[$obj->pid] .= ','.$obj->code;
  1023. }
  1024. $i++;
  1025. }
  1026. $this->db->free($resql);
  1027. } else {
  1028. dol_print_error($this->db);
  1029. }
  1030. return $arrayroles;
  1031. }
  1032. /**
  1033. * Return list of id of contacts of task
  1034. *
  1035. * @param string $source Source
  1036. * @return array Array of id of contacts
  1037. */
  1038. public function getListContactId($source = 'internal')
  1039. {
  1040. $contactAlreadySelected = array();
  1041. $tab = $this->liste_contact(-1, $source);
  1042. //var_dump($tab);
  1043. $num = count($tab);
  1044. $i = 0;
  1045. while ($i < $num) {
  1046. if ($source == 'thirdparty') {
  1047. $contactAlreadySelected[$i] = $tab[$i]['socid'];
  1048. } else {
  1049. $contactAlreadySelected[$i] = $tab[$i]['id'];
  1050. }
  1051. $i++;
  1052. }
  1053. return $contactAlreadySelected;
  1054. }
  1055. /**
  1056. * Add time spent
  1057. *
  1058. * @param User $user User object
  1059. * @param int $notrigger 0=launch triggers after, 1=disable triggers
  1060. * @return int <=0 if KO, >0 if OK
  1061. */
  1062. public function addTimeSpent($user, $notrigger = 0)
  1063. {
  1064. global $conf, $langs;
  1065. dol_syslog(get_class($this)."::addTimeSpent", LOG_DEBUG);
  1066. $ret = 0;
  1067. $now = dol_now();
  1068. // Check parameters
  1069. if (!is_object($user)) {
  1070. dol_print_error('', "Method addTimeSpent was called with wrong parameter user");
  1071. return -1;
  1072. }
  1073. // Clean parameters
  1074. if (isset($this->timespent_note)) {
  1075. $this->timespent_note = trim($this->timespent_note);
  1076. }
  1077. if (empty($this->timespent_datehour)) {
  1078. $this->timespent_datehour = $this->timespent_date;
  1079. }
  1080. if (!empty($conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS)) {
  1081. require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
  1082. $restrictBefore = dol_time_plus_duree(dol_now(), - $conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS, 'm');
  1083. if ($this->timespent_date < $restrictBefore) {
  1084. $this->error = $langs->trans('TimeRecordingRestrictedToNMonthsBack', $conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS);
  1085. $this->errors[] = $this->error;
  1086. return -1;
  1087. }
  1088. }
  1089. $this->db->begin();
  1090. $sql = "INSERT INTO ".MAIN_DB_PREFIX."projet_task_time (";
  1091. $sql .= "fk_task";
  1092. $sql .= ", task_date";
  1093. $sql .= ", task_datehour";
  1094. $sql .= ", task_date_withhour";
  1095. $sql .= ", task_duration";
  1096. $sql .= ", fk_user";
  1097. $sql .= ", fk_product";
  1098. $sql .= ", note";
  1099. $sql .= ", datec";
  1100. $sql .= ") VALUES (";
  1101. $sql .= ((int) $this->id);
  1102. $sql .= ", '".$this->db->idate($this->timespent_date)."'";
  1103. $sql .= ", '".$this->db->idate($this->timespent_datehour)."'";
  1104. $sql .= ", ".(empty($this->timespent_withhour) ? 0 : 1);
  1105. $sql .= ", ".((int) $this->timespent_duration);
  1106. $sql .= ", ".((int) $this->timespent_fk_user);
  1107. $sql .= ", ".((int) $this->timespent_fk_product);
  1108. $sql .= ", ".(isset($this->timespent_note) ? "'".$this->db->escape($this->timespent_note)."'" : "null");
  1109. $sql .= ", '".$this->db->idate($now)."'";
  1110. $sql .= ")";
  1111. $resql = $this->db->query($sql);
  1112. if ($resql) {
  1113. $tasktime_id = $this->db->last_insert_id(MAIN_DB_PREFIX."projet_task_time");
  1114. $ret = $tasktime_id;
  1115. $this->timespent_id = $ret;
  1116. if (!$notrigger) {
  1117. // Call trigger
  1118. $result = $this->call_trigger('TASK_TIMESPENT_CREATE', $user);
  1119. if ($result < 0) {
  1120. $ret = -1;
  1121. }
  1122. // End call triggers
  1123. }
  1124. } else {
  1125. $this->error = $this->db->lasterror();
  1126. $ret = -1;
  1127. }
  1128. if ($ret > 0) {
  1129. // Recalculate amount of time spent for task and update denormalized field
  1130. $sql = "UPDATE ".MAIN_DB_PREFIX."projet_task";
  1131. $sql .= " SET duration_effective = (SELECT SUM(task_duration) FROM ".MAIN_DB_PREFIX."projet_task_time as ptt where ptt.fk_task = ".((int) $this->id).")";
  1132. if (isset($this->progress)) {
  1133. $sql .= ", progress = ".((float) $this->progress); // Do not overwrite value if not provided
  1134. }
  1135. $sql .= " WHERE rowid = ".((int) $this->id);
  1136. dol_syslog(get_class($this)."::addTimeSpent", LOG_DEBUG);
  1137. if (!$this->db->query($sql)) {
  1138. $this->error = $this->db->lasterror();
  1139. $ret = -2;
  1140. }
  1141. // Update hourly rate of this time spent entry
  1142. $sql = "UPDATE ".MAIN_DB_PREFIX."projet_task_time";
  1143. $sql .= " SET thm = (SELECT thm FROM ".MAIN_DB_PREFIX."user WHERE rowid = ".((int) $this->timespent_fk_user).")"; // set average hour rate of user
  1144. $sql .= " WHERE rowid = ".((int) $tasktime_id);
  1145. dol_syslog(get_class($this)."::addTimeSpent", LOG_DEBUG);
  1146. if (!$this->db->query($sql)) {
  1147. $this->error = $this->db->lasterror();
  1148. $ret = -2;
  1149. }
  1150. }
  1151. if ($ret > 0) {
  1152. $this->db->commit();
  1153. } else {
  1154. $this->db->rollback();
  1155. }
  1156. return $ret;
  1157. }
  1158. /**
  1159. * Fetch records of time spent of this task
  1160. *
  1161. * @param string $morewherefilter Add more filter into where SQL request (must start with ' AND ...')
  1162. * @return int <0 if KO, array of time spent if OK
  1163. */
  1164. public function fetchTimeSpentOnTask($morewherefilter = '')
  1165. {
  1166. global $langs;
  1167. $arrayres = array();
  1168. $sql = "SELECT";
  1169. $sql .= " s.rowid as socid,";
  1170. $sql .= " s.nom as thirdparty_name,";
  1171. $sql .= " s.email as thirdparty_email,";
  1172. $sql .= " ptt.rowid,";
  1173. $sql .= " ptt.fk_task,";
  1174. $sql .= " ptt.task_date,";
  1175. $sql .= " ptt.task_datehour,";
  1176. $sql .= " ptt.task_date_withhour,";
  1177. $sql .= " ptt.task_duration,";
  1178. $sql .= " ptt.fk_user,";
  1179. $sql .= " ptt.note,";
  1180. $sql .= " ptt.thm,";
  1181. $sql .= " pt.rowid as task_id,";
  1182. $sql .= " pt.ref as task_ref,";
  1183. $sql .= " pt.label as task_label,";
  1184. $sql .= " p.rowid as project_id,";
  1185. $sql .= " p.ref as project_ref,";
  1186. $sql .= " p.title as project_label,";
  1187. $sql .= " p.public as public";
  1188. $sql .= " FROM ".MAIN_DB_PREFIX."projet_task_time as ptt, ".MAIN_DB_PREFIX."projet_task as pt, ".MAIN_DB_PREFIX."projet as p";
  1189. $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
  1190. $sql .= " WHERE ptt.fk_task = pt.rowid AND pt.fk_projet = p.rowid";
  1191. $sql .= " AND pt.rowid = ".((int) $this->id);
  1192. $sql .= " AND pt.entity IN (".getEntity('project').")";
  1193. if ($morewherefilter) {
  1194. $sql .= $morewherefilter;
  1195. }
  1196. dol_syslog(get_class($this)."::fetchAllTimeSpent", LOG_DEBUG);
  1197. $resql = $this->db->query($sql);
  1198. if ($resql) {
  1199. $num = $this->db->num_rows($resql);
  1200. $i = 0;
  1201. while ($i < $num) {
  1202. $obj = $this->db->fetch_object($resql);
  1203. $newobj = new stdClass();
  1204. $newobj->socid = $obj->socid;
  1205. $newobj->thirdparty_name = $obj->thirdparty_name;
  1206. $newobj->thirdparty_email = $obj->thirdparty_email;
  1207. $newobj->fk_project = $obj->project_id;
  1208. $newobj->project_ref = $obj->project_ref;
  1209. $newobj->project_label = $obj->project_label;
  1210. $newobj->public = $obj->project_public;
  1211. $newobj->fk_task = $obj->task_id;
  1212. $newobj->task_ref = $obj->task_ref;
  1213. $newobj->task_label = $obj->task_label;
  1214. $newobj->timespent_line_id = $obj->rowid;
  1215. $newobj->timespent_line_date = $this->db->jdate($obj->task_date);
  1216. $newobj->timespent_line_datehour = $this->db->jdate($obj->task_datehour);
  1217. $newobj->timespent_line_withhour = $obj->task_date_withhour;
  1218. $newobj->timespent_line_duration = $obj->task_duration;
  1219. $newobj->timespent_line_fk_user = $obj->fk_user;
  1220. $newobj->timespent_line_thm = $obj->thm; // hourly rate
  1221. $newobj->timespent_line_note = $obj->note;
  1222. $arrayres[] = $newobj;
  1223. $i++;
  1224. }
  1225. $this->db->free($resql);
  1226. $this->lines = $arrayres;
  1227. return 1;
  1228. } else {
  1229. dol_print_error($this->db);
  1230. $this->error = "Error ".$this->db->lasterror();
  1231. return -1;
  1232. }
  1233. }
  1234. /**
  1235. * Calculate total of time spent for task
  1236. *
  1237. * @param User|int $userobj Filter on user. null or 0=No filter
  1238. * @param string $morewherefilter Add more filter into where SQL request (must start with ' AND ...')
  1239. * @return array Array of info for task array('min_date', 'max_date', 'total_duration', 'total_amount', 'nblines', 'nblinesnull')
  1240. */
  1241. public function getSummaryOfTimeSpent($userobj = null, $morewherefilter = '')
  1242. {
  1243. global $langs;
  1244. if (is_object($userobj)) {
  1245. $userid = $userobj->id;
  1246. } else {
  1247. $userid = $userobj; // old method
  1248. }
  1249. $id = $this->id;
  1250. if (empty($id) && empty($userid)) {
  1251. dol_syslog("getSummaryOfTimeSpent called on a not loaded task without user param defined", LOG_ERR);
  1252. return -1;
  1253. }
  1254. $result = array();
  1255. $sql = "SELECT";
  1256. $sql .= " MIN(t.task_datehour) as min_date,";
  1257. $sql .= " MAX(t.task_datehour) as max_date,";
  1258. $sql .= " SUM(t.task_duration) as total_duration,";
  1259. $sql .= " SUM(t.task_duration / 3600 * ".$this->db->ifsql("t.thm IS NULL", 0, "t.thm").") as total_amount,";
  1260. $sql .= " COUNT(t.rowid) as nblines,";
  1261. $sql .= " SUM(".$this->db->ifsql("t.thm IS NULL", 1, 0).") as nblinesnull";
  1262. $sql .= " FROM ".MAIN_DB_PREFIX."projet_task_time as t";
  1263. $sql .= " WHERE 1 = 1";
  1264. if ($morewherefilter) {
  1265. $sql .= $morewherefilter;
  1266. }
  1267. if ($id > 0) {
  1268. $sql .= " AND t.fk_task = ".((int) $id);
  1269. }
  1270. if ($userid > 0) {
  1271. $sql .= " AND t.fk_user = ".((int) $userid);
  1272. }
  1273. dol_syslog(get_class($this)."::getSummaryOfTimeSpent", LOG_DEBUG);
  1274. $resql = $this->db->query($sql);
  1275. if ($resql) {
  1276. $obj = $this->db->fetch_object($resql);
  1277. $result['min_date'] = $obj->min_date; // deprecated. use the ->timespent_xxx instead
  1278. $result['max_date'] = $obj->max_date; // deprecated. use the ->timespent_xxx instead
  1279. $result['total_duration'] = $obj->total_duration; // deprecated. use the ->timespent_xxx instead
  1280. $this->timespent_min_date = $this->db->jdate($obj->min_date);
  1281. $this->timespent_max_date = $this->db->jdate($obj->max_date);
  1282. $this->timespent_total_duration = $obj->total_duration;
  1283. $this->timespent_total_amount = $obj->total_amount;
  1284. $this->timespent_nblinesnull = ($obj->nblinesnull ? $obj->nblinesnull : 0);
  1285. $this->timespent_nblines = ($obj->nblines ? $obj->nblines : 0);
  1286. $this->db->free($resql);
  1287. } else {
  1288. dol_print_error($this->db);
  1289. }
  1290. return $result;
  1291. }
  1292. /**
  1293. * Calculate quantity and value of time consumed using the thm (hourly amount value of work for user entering time)
  1294. *
  1295. * @param User $fuser Filter on a dedicated user
  1296. * @param string $dates Start date (ex 00:00:00)
  1297. * @param string $datee End date (ex 23:59:59)
  1298. * @return array Array of info for task array('amount','nbseconds','nblinesnull')
  1299. */
  1300. public function getSumOfAmount($fuser = '', $dates = '', $datee = '')
  1301. {
  1302. global $langs;
  1303. if (empty($id)) {
  1304. $id = $this->id;
  1305. }
  1306. $result = array();
  1307. $sql = "SELECT";
  1308. $sql .= " SUM(t.task_duration) as nbseconds,";
  1309. $sql .= " SUM(t.task_duration / 3600 * ".$this->db->ifsql("t.thm IS NULL", 0, "t.thm").") as amount, SUM(".$this->db->ifsql("t.thm IS NULL", 1, 0).") as nblinesnull";
  1310. $sql .= " FROM ".MAIN_DB_PREFIX."projet_task_time as t";
  1311. $sql .= " WHERE t.fk_task = ".((int) $id);
  1312. if (is_object($fuser) && $fuser->id > 0) {
  1313. $sql .= " AND fk_user = ".((int) $fuser->id);
  1314. }
  1315. if ($dates > 0) {
  1316. $datefieldname = "task_datehour";
  1317. $sql .= " AND (".$datefieldname." >= '".$this->db->idate($dates)."' OR ".$datefieldname." IS NULL)";
  1318. }
  1319. if ($datee > 0) {
  1320. $datefieldname = "task_datehour";
  1321. $sql .= " AND (".$datefieldname." <= '".$this->db->idate($datee)."' OR ".$datefieldname." IS NULL)";
  1322. }
  1323. //print $sql;
  1324. dol_syslog(get_class($this)."::getSumOfAmount", LOG_DEBUG);
  1325. $resql = $this->db->query($sql);
  1326. if ($resql) {
  1327. $obj = $this->db->fetch_object($resql);
  1328. $result['amount'] = $obj->amount;
  1329. $result['nbseconds'] = $obj->nbseconds;
  1330. $result['nblinesnull'] = $obj->nblinesnull;
  1331. $this->db->free($resql);
  1332. return $result;
  1333. } else {
  1334. dol_print_error($this->db);
  1335. return $result;
  1336. }
  1337. }
  1338. /**
  1339. * Load properties of timespent of a task from the time spent ID.
  1340. *
  1341. * @param int $id Id in time spent table
  1342. * @return int <0 if KO, >0 if OK
  1343. */
  1344. public function fetchTimeSpent($id)
  1345. {
  1346. global $langs;
  1347. $sql = "SELECT";
  1348. $sql .= " t.rowid,";
  1349. $sql .= " t.fk_task,";
  1350. $sql .= " t.task_date,";
  1351. $sql .= " t.task_datehour,";
  1352. $sql .= " t.task_date_withhour,";
  1353. $sql .= " t.task_duration,";
  1354. $sql .= " t.fk_user,";
  1355. $sql .= " t.fk_product,";
  1356. $sql .= " t.thm,";
  1357. $sql .= " t.note";
  1358. $sql .= " FROM ".MAIN_DB_PREFIX."projet_task_time as t";
  1359. $sql .= " WHERE t.rowid = ".((int) $id);
  1360. dol_syslog(get_class($this)."::fetchTimeSpent", LOG_DEBUG);
  1361. $resql = $this->db->query($sql);
  1362. if ($resql) {
  1363. if ($this->db->num_rows($resql)) {
  1364. $obj = $this->db->fetch_object($resql);
  1365. $this->timespent_id = $obj->rowid;
  1366. $this->id = $obj->fk_task;
  1367. $this->timespent_date = $this->db->jdate($obj->task_date);
  1368. $this->timespent_datehour = $this->db->jdate($obj->task_datehour);
  1369. $this->timespent_withhour = $obj->task_date_withhour;
  1370. $this->timespent_duration = $obj->task_duration;
  1371. $this->timespent_fk_user = $obj->fk_user;
  1372. $this->timespent_fk_product = $obj->fk_product;
  1373. $this->timespent_thm = $obj->thm; // hourly rate
  1374. $this->timespent_note = $obj->note;
  1375. }
  1376. $this->db->free($resql);
  1377. return 1;
  1378. } else {
  1379. $this->error = "Error ".$this->db->lasterror();
  1380. return -1;
  1381. }
  1382. }
  1383. /**
  1384. * Load all records of time spent
  1385. *
  1386. * @param User $userobj User object
  1387. * @param string $morewherefilter Add more filter into where SQL request (must start with ' AND ...')
  1388. * @return int <0 if KO, array of time spent if OK
  1389. */
  1390. public function fetchAllTimeSpent(User $userobj, $morewherefilter = '')
  1391. {
  1392. global $langs;
  1393. $arrayres = array();
  1394. $sql = "SELECT";
  1395. $sql .= " s.rowid as socid,";
  1396. $sql .= " s.nom as thirdparty_name,";
  1397. $sql .= " s.email as thirdparty_email,";
  1398. $sql .= " ptt.rowid,";
  1399. $sql .= " ptt.fk_task,";
  1400. $sql .= " ptt.task_date,";
  1401. $sql .= " ptt.task_datehour,";
  1402. $sql .= " ptt.task_date_withhour,";
  1403. $sql .= " ptt.task_duration,";
  1404. $sql .= " ptt.fk_user,";
  1405. $sql .= " ptt.note,";
  1406. $sql .= " ptt.thm,";
  1407. $sql .= " pt.rowid as task_id,";
  1408. $sql .= " pt.ref as task_ref,";
  1409. $sql .= " pt.label as task_label,";
  1410. $sql .= " p.rowid as project_id,";
  1411. $sql .= " p.ref as project_ref,";
  1412. $sql .= " p.title as project_label,";
  1413. $sql .= " p.public as public";
  1414. $sql .= " FROM ".MAIN_DB_PREFIX."projet_task_time as ptt, ".MAIN_DB_PREFIX."projet_task as pt, ".MAIN_DB_PREFIX."projet as p";
  1415. $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
  1416. $sql .= " WHERE ptt.fk_task = pt.rowid AND pt.fk_projet = p.rowid";
  1417. $sql .= " AND ptt.fk_user = ".((int) $userobj->id);
  1418. $sql .= " AND pt.entity IN (".getEntity('project').")";
  1419. if ($morewherefilter) {
  1420. $sql .= $morewherefilter;
  1421. }
  1422. dol_syslog(get_class($this)."::fetchAllTimeSpent", LOG_DEBUG);
  1423. $resql = $this->db->query($sql);
  1424. if ($resql) {
  1425. $num = $this->db->num_rows($resql);
  1426. $i = 0;
  1427. while ($i < $num) {
  1428. $obj = $this->db->fetch_object($resql);
  1429. $newobj = new stdClass();
  1430. $newobj->socid = $obj->socid;
  1431. $newobj->thirdparty_name = $obj->thirdparty_name;
  1432. $newobj->thirdparty_email = $obj->thirdparty_email;
  1433. $newobj->fk_project = $obj->project_id;
  1434. $newobj->project_ref = $obj->project_ref;
  1435. $newobj->project_label = $obj->project_label;
  1436. $newobj->public = $obj->project_public;
  1437. $newobj->fk_task = $obj->task_id;
  1438. $newobj->task_ref = $obj->task_ref;
  1439. $newobj->task_label = $obj->task_label;
  1440. $newobj->timespent_id = $obj->rowid;
  1441. $newobj->timespent_date = $this->db->jdate($obj->task_date);
  1442. $newobj->timespent_datehour = $this->db->jdate($obj->task_datehour);
  1443. $newobj->timespent_withhour = $obj->task_date_withhour;
  1444. $newobj->timespent_duration = $obj->task_duration;
  1445. $newobj->timespent_fk_user = $obj->fk_user;
  1446. $newobj->timespent_thm = $obj->thm; // hourly rate
  1447. $newobj->timespent_note = $obj->note;
  1448. $arrayres[] = $newobj;
  1449. $i++;
  1450. }
  1451. $this->db->free($resql);
  1452. } else {
  1453. dol_print_error($this->db);
  1454. $this->error = "Error ".$this->db->lasterror();
  1455. return -1;
  1456. }
  1457. return $arrayres;
  1458. }
  1459. /**
  1460. * Update time spent
  1461. *
  1462. * @param User $user User id
  1463. * @param int $notrigger 0=launch triggers after, 1=disable triggers
  1464. * @return int <0 if KO, >0 if OK
  1465. */
  1466. public function updateTimeSpent($user, $notrigger = 0)
  1467. {
  1468. global $conf, $langs;
  1469. $ret = 0;
  1470. // Check parameters
  1471. if ($this->timespent_date == '') {
  1472. $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentities("Date"));
  1473. return -1;
  1474. }
  1475. if (!($this->timespent_fk_user > 0)) {
  1476. $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentities("User"));
  1477. return -1;
  1478. }
  1479. // Clean parameters
  1480. if (empty($this->timespent_datehour)) {
  1481. $this->timespent_datehour = $this->timespent_date;
  1482. }
  1483. if (isset($this->timespent_note)) {
  1484. $this->timespent_note = trim($this->timespent_note);
  1485. }
  1486. if (!empty($conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS)) {
  1487. require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
  1488. $restrictBefore = dol_time_plus_duree(dol_now(), - $conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS, 'm');
  1489. if ($this->timespent_date < $restrictBefore) {
  1490. $this->error = $langs->trans('TimeRecordingRestrictedToNMonthsBack', $conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS);
  1491. $this->errors[] = $this->error;
  1492. return -1;
  1493. }
  1494. }
  1495. $this->db->begin();
  1496. $sql = "UPDATE ".MAIN_DB_PREFIX."projet_task_time SET";
  1497. $sql .= " task_date = '".$this->db->idate($this->timespent_date)."',";
  1498. $sql .= " task_datehour = '".$this->db->idate($this->timespent_datehour)."',";
  1499. $sql .= " task_date_withhour = ".(empty($this->timespent_withhour) ? 0 : 1).",";
  1500. $sql .= " task_duration = ".((int) $this->timespent_duration).",";
  1501. $sql .= " fk_user = ".((int) $this->timespent_fk_user).",";
  1502. $sql .= " fk_product = ".((int) $this->timespent_fk_product).",";
  1503. $sql .= " note = ".(isset($this->timespent_note) ? "'".$this->db->escape($this->timespent_note)."'" : "null");
  1504. $sql .= " WHERE rowid = ".((int) $this->timespent_id);
  1505. dol_syslog(get_class($this)."::updateTimeSpent", LOG_DEBUG);
  1506. if ($this->db->query($sql)) {
  1507. if (!$notrigger) {
  1508. // Call trigger
  1509. $result = $this->call_trigger('TASK_TIMESPENT_MODIFY', $user);
  1510. if ($result < 0) {
  1511. $this->db->rollback();
  1512. $ret = -1;
  1513. } else {
  1514. $ret = 1;
  1515. }
  1516. // End call triggers
  1517. } else {
  1518. $ret = 1;
  1519. }
  1520. } else {
  1521. $this->error = $this->db->lasterror();
  1522. $this->db->rollback();
  1523. $ret = -1;
  1524. }
  1525. if ($ret == 1 && (($this->timespent_old_duration != $this->timespent_duration) || !empty($conf->global->TIMESPENT_ALWAYS_UPDATE_THM))) {
  1526. if ($this->timespent_old_duration != $this->timespent_duration) {
  1527. // Recalculate amount of time spent for task and update denormalized field
  1528. $sql = "UPDATE " . MAIN_DB_PREFIX . "projet_task";
  1529. $sql .= " SET duration_effective = (SELECT SUM(task_duration) FROM " . MAIN_DB_PREFIX . "projet_task_time as ptt where ptt.fk_task = " . ((int) $this->id) . ")";
  1530. if (isset($this->progress)) {
  1531. $sql .= ", progress = " . ((float) $this->progress); // Do not overwrite value if not provided
  1532. }
  1533. $sql .= " WHERE rowid = " . ((int) $this->id);
  1534. dol_syslog(get_class($this) . "::updateTimeSpent", LOG_DEBUG);
  1535. if (!$this->db->query($sql)) {
  1536. $this->error = $this->db->lasterror();
  1537. $this->db->rollback();
  1538. $ret = -2;
  1539. }
  1540. }
  1541. // Update hourly rate of this time spent entry, but only if it was not set initialy
  1542. $sql = "UPDATE ".MAIN_DB_PREFIX."projet_task_time";
  1543. $sql .= " SET thm = (SELECT thm FROM ".MAIN_DB_PREFIX."user WHERE rowid = ".((int) $this->timespent_fk_user).")"; // set average hour rate of user
  1544. $sql .= " WHERE rowid = ".((int) $this->timespent_id);
  1545. if (empty($conf->global->TIMESPENT_ALWAYS_UPDATE_THM)) { // then if not empty we always update, in case of new thm for user, or change user of task time line
  1546. $sql .= " AND (thm IS NULL OR thm = 0)";
  1547. }
  1548. dol_syslog(get_class($this)."::addTimeSpent", LOG_DEBUG);
  1549. if (!$this->db->query($sql)) {
  1550. $this->error = $this->db->lasterror();
  1551. $ret = -2;
  1552. }
  1553. }
  1554. if ($ret >= 0) {
  1555. $this->db->commit();
  1556. }
  1557. return $ret;
  1558. }
  1559. /**
  1560. * Delete time spent
  1561. *
  1562. * @param User $user User that delete
  1563. * @param int $notrigger 0=launch triggers after, 1=disable triggers
  1564. * @return int <0 if KO, >0 if OK
  1565. */
  1566. public function delTimeSpent($user, $notrigger = 0)
  1567. {
  1568. global $conf, $langs;
  1569. $error = 0;
  1570. if (!empty($conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS)) {
  1571. require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
  1572. $restrictBefore = dol_time_plus_duree(dol_now(), - $conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS, 'm');
  1573. if ($this->timespent_date < $restrictBefore) {
  1574. $this->error = $langs->trans('TimeRecordingRestrictedToNMonthsBack', $conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS);
  1575. $this->errors[] = $this->error;
  1576. return -1;
  1577. }
  1578. }
  1579. $this->db->begin();
  1580. if (!$notrigger) {
  1581. // Call trigger
  1582. $result = $this->call_trigger('TASK_TIMESPENT_DELETE', $user);
  1583. if ($result < 0) {
  1584. $error++;
  1585. }
  1586. // End call triggers
  1587. }
  1588. if (!$error) {
  1589. $sql = "DELETE FROM ".MAIN_DB_PREFIX."projet_task_time";
  1590. $sql .= " WHERE rowid = ".((int) $this->timespent_id);
  1591. dol_syslog(get_class($this)."::delTimeSpent", LOG_DEBUG);
  1592. $resql = $this->db->query($sql);
  1593. if (!$resql) {
  1594. $error++; $this->errors[] = "Error ".$this->db->lasterror();
  1595. }
  1596. }
  1597. if (!$error) {
  1598. $sql = "UPDATE ".MAIN_DB_PREFIX."projet_task";
  1599. $sql .= " SET duration_effective = duration_effective - ".$this->db->escape($this->timespent_duration ? $this->timespent_duration : 0);
  1600. $sql .= " WHERE rowid = ".((int) $this->id);
  1601. dol_syslog(get_class($this)."::delTimeSpent", LOG_DEBUG);
  1602. if ($this->db->query($sql)) {
  1603. $result = 0;
  1604. } else {
  1605. $this->error = $this->db->lasterror();
  1606. $result = -2;
  1607. }
  1608. }
  1609. // Commit or rollback
  1610. if ($error) {
  1611. foreach ($this->errors as $errmsg) {
  1612. dol_syslog(get_class($this)."::delTimeSpent ".$errmsg, LOG_ERR);
  1613. $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
  1614. }
  1615. $this->db->rollback();
  1616. return -1 * $error;
  1617. } else {
  1618. $this->db->commit();
  1619. return 1;
  1620. }
  1621. }
  1622. /** Load an object from its id and create a new one in database
  1623. *
  1624. * @param User $user User making the clone
  1625. * @param int $fromid Id of object to clone
  1626. * @param int $project_id Id of project to attach clone task
  1627. * @param int $parent_task_id Id of task to attach clone task
  1628. * @param bool $clone_change_dt recalculate date of task regarding new project start date
  1629. * @param bool $clone_affectation clone affectation of project
  1630. * @param bool $clone_time clone time of project
  1631. * @param bool $clone_file clone file of project
  1632. * @param bool $clone_note clone note of project
  1633. * @param bool $clone_prog clone progress of project
  1634. * @return int New id of clone
  1635. */
  1636. public function createFromClone(User $user, $fromid, $project_id, $parent_task_id, $clone_change_dt = false, $clone_affectation = false, $clone_time = false, $clone_file = false, $clone_note = false, $clone_prog = false)
  1637. {
  1638. global $langs, $conf;
  1639. $error = 0;
  1640. //Use 00:00 of today if time is use on task.
  1641. $now = dol_mktime(0, 0, 0, dol_print_date(dol_now(), '%m'), dol_print_date(dol_now(), '%d'), dol_print_date(dol_now(), '%Y'));
  1642. $datec = $now;
  1643. $clone_task = new Task($this->db);
  1644. $origin_task = new Task($this->db);
  1645. $clone_task->context['createfromclone'] = 'createfromclone';
  1646. $this->db->begin();
  1647. // Load source object
  1648. $clone_task->fetch($fromid);
  1649. $clone_task->fetch_optionals();
  1650. //var_dump($clone_task->array_options);exit;
  1651. $origin_task->fetch($fromid);
  1652. $defaultref = '';
  1653. $obj = empty($conf->global->PROJECT_TASK_ADDON) ? 'mod_task_simple' : $conf->global->PROJECT_TASK_ADDON;
  1654. if (!empty($conf->global->PROJECT_TASK_ADDON) && is_readable(DOL_DOCUMENT_ROOT."/core/modules/project/task/".$conf->global->PROJECT_TASK_ADDON.".php")) {
  1655. require_once DOL_DOCUMENT_ROOT."/core/modules/project/task/".$conf->global->PROJECT_TASK_ADDON.'.php';
  1656. $modTask = new $obj;
  1657. $defaultref = $modTask->getNextValue(0, $clone_task);
  1658. }
  1659. $ori_project_id = $clone_task->fk_project;
  1660. $clone_task->id = 0;
  1661. $clone_task->ref = $defaultref;
  1662. $clone_task->fk_project = $project_id;
  1663. $clone_task->fk_task_parent = $parent_task_id;
  1664. $clone_task->date_c = $datec;
  1665. $clone_task->planned_workload = $origin_task->planned_workload;
  1666. $clone_task->rang = $origin_task->rang;
  1667. //Manage Task Date
  1668. if ($clone_change_dt) {
  1669. $projectstatic = new Project($this->db);
  1670. $projectstatic->fetch($ori_project_id);
  1671. //Origin project strat date
  1672. $orign_project_dt_start = $projectstatic->date_start;
  1673. //Calcultate new task start date with difference between origin proj start date and origin task start date
  1674. if (!empty($clone_task->date_start)) {
  1675. $clone_task->date_start = $now + $clone_task->date_start - $orign_project_dt_start;
  1676. }
  1677. //Calcultate new task end date with difference between origin proj end date and origin task end date
  1678. if (!empty($clone_task->date_end)) {
  1679. $clone_task->date_end = $now + $clone_task->date_end - $orign_project_dt_start;
  1680. }
  1681. }
  1682. if (!$clone_prog) {
  1683. $clone_task->progress = 0;
  1684. }
  1685. // Create clone
  1686. $result = $clone_task->create($user);
  1687. // Other options
  1688. if ($result < 0) {
  1689. $this->error = $clone_task->error;
  1690. $error++;
  1691. }
  1692. // End
  1693. if (!$error) {
  1694. $clone_task_id = $clone_task->id;
  1695. $clone_task_ref = $clone_task->ref;
  1696. //Note Update
  1697. if (!$clone_note) {
  1698. $clone_task->note_private = '';
  1699. $clone_task->note_public = '';
  1700. } else {
  1701. $this->db->begin();
  1702. $res = $clone_task->update_note(dol_html_entity_decode($clone_task->note_public, ENT_QUOTES | ENT_HTML5), '_public');
  1703. if ($res < 0) {
  1704. $this->error .= $clone_task->error;
  1705. $error++;
  1706. $this->db->rollback();
  1707. } else {
  1708. $this->db->commit();
  1709. }
  1710. $this->db->begin();
  1711. $res = $clone_task->update_note(dol_html_entity_decode($clone_task->note_private, ENT_QUOTES | ENT_HTML5), '_private');
  1712. if ($res < 0) {
  1713. $this->error .= $clone_task->error;
  1714. $error++;
  1715. $this->db->rollback();
  1716. } else {
  1717. $this->db->commit();
  1718. }
  1719. }
  1720. //Duplicate file
  1721. if ($clone_file) {
  1722. require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
  1723. //retrieve project origin ref to know folder to copy
  1724. $projectstatic = new Project($this->db);
  1725. $projectstatic->fetch($ori_project_id);
  1726. $ori_project_ref = $projectstatic->ref;
  1727. if ($ori_project_id != $project_id) {
  1728. $projectstatic->fetch($project_id);
  1729. $clone_project_ref = $projectstatic->ref;
  1730. } else {
  1731. $clone_project_ref = $ori_project_ref;
  1732. }
  1733. $clone_task_dir = $conf->project->dir_output."/".dol_sanitizeFileName($clone_project_ref)."/".dol_sanitizeFileName($clone_task_ref);
  1734. $ori_task_dir = $conf->project->dir_output."/".dol_sanitizeFileName($ori_project_ref)."/".dol_sanitizeFileName($fromid);
  1735. $filearray = dol_dir_list($ori_task_dir, "files", 0, '', '(\.meta|_preview.*\.png)$', '', SORT_ASC, 1);
  1736. foreach ($filearray as $key => $file) {
  1737. if (!file_exists($clone_task_dir)) {
  1738. if (dol_mkdir($clone_task_dir) < 0) {
  1739. $this->error .= $langs->trans('ErrorInternalErrorDetected').':dol_mkdir';
  1740. $error++;
  1741. }
  1742. }
  1743. $rescopy = dol_copy($ori_task_dir.'/'.$file['name'], $clone_task_dir.'/'.$file['name'], 0, 1);
  1744. if (is_numeric($rescopy) && $rescopy < 0) {
  1745. $this->error .= $langs->trans("ErrorFailToCopyFile", $ori_task_dir.'/'.$file['name'], $clone_task_dir.'/'.$file['name']);
  1746. $error++;
  1747. }
  1748. }
  1749. }
  1750. // clone affectation
  1751. if ($clone_affectation) {
  1752. $origin_task = new Task($this->db);
  1753. $origin_task->fetch($fromid);
  1754. foreach (array('internal', 'external') as $source) {
  1755. $tab = $origin_task->liste_contact(-1, $source);
  1756. $num = count($tab);
  1757. $i = 0;
  1758. while ($i < $num) {
  1759. $clone_task->add_contact($tab[$i]['id'], $tab[$i]['code'], $tab[$i]['source']);
  1760. if ($clone_task->error == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
  1761. $langs->load("errors");
  1762. $this->error .= $langs->trans("ErrorThisContactIsAlreadyDefinedAsThisType");
  1763. $error++;
  1764. } else {
  1765. if ($clone_task->error != '') {
  1766. $this->error .= $clone_task->error;
  1767. $error++;
  1768. }
  1769. }
  1770. $i++;
  1771. }
  1772. }
  1773. }
  1774. if ($clone_time) {
  1775. //TODO clone time of affectation
  1776. }
  1777. }
  1778. unset($clone_task->context['createfromclone']);
  1779. if (!$error) {
  1780. $this->db->commit();
  1781. return $clone_task_id;
  1782. } else {
  1783. $this->db->rollback();
  1784. dol_syslog(get_class($this)."::createFromClone nbError: ".$error." error : ".$this->error, LOG_ERR);
  1785. return -1;
  1786. }
  1787. }
  1788. /**
  1789. * Return status label of object
  1790. *
  1791. * @param integer $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto
  1792. * @return string Label
  1793. */
  1794. public function getLibStatut($mode = 0)
  1795. {
  1796. return $this->LibStatut($this->fk_statut, $mode);
  1797. }
  1798. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  1799. /**
  1800. * Return status label for an object
  1801. *
  1802. * @param int $status Id status
  1803. * @param integer $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto
  1804. * @return string Label
  1805. */
  1806. public function LibStatut($status, $mode = 0)
  1807. {
  1808. // phpcs:enable
  1809. global $langs;
  1810. // list of Statut of the task
  1811. $this->statuts[0] = 'Draft';
  1812. $this->statuts[1] = 'ToDo';
  1813. $this->statuts[2] = 'Running';
  1814. $this->statuts[3] = 'Finish';
  1815. $this->statuts[4] = 'Transfered';
  1816. $this->statuts_short[0] = 'Draft';
  1817. $this->statuts_short[1] = 'ToDo';
  1818. $this->statuts_short[2] = 'Running';
  1819. $this->statuts_short[3] = 'Completed';
  1820. $this->statuts_short[4] = 'Transfered';
  1821. if ($mode == 0) {
  1822. return $langs->trans($this->statuts[$status]);
  1823. } elseif ($mode == 1) {
  1824. return $langs->trans($this->statuts_short[$status]);
  1825. } elseif ($mode == 2) {
  1826. if ($status == 0) {
  1827. return img_picto($langs->trans($this->statuts_short[$status]), 'statut0').' '.$langs->trans($this->statuts_short[$status]);
  1828. } elseif ($status == 1) {
  1829. return img_picto($langs->trans($this->statuts_short[$status]), 'statut1').' '.$langs->trans($this->statuts_short[$status]);
  1830. } elseif ($status == 2) {
  1831. return img_picto($langs->trans($this->statuts_short[$status]), 'statut3').' '.$langs->trans($this->statuts_short[$status]);
  1832. } elseif ($status == 3) {
  1833. return img_picto($langs->trans($this->statuts_short[$status]), 'statut6').' '.$langs->trans($this->statuts_short[$status]);
  1834. } elseif ($status == 4) {
  1835. return img_picto($langs->trans($this->statuts_short[$status]), 'statut6').' '.$langs->trans($this->statuts_short[$status]);
  1836. } elseif ($status == 5) {
  1837. return img_picto($langs->trans($this->statuts_short[$status]), 'statut5').' '.$langs->trans($this->statuts_short[$status]);
  1838. }
  1839. } elseif ($mode == 3) {
  1840. if ($status == 0) {
  1841. return img_picto($langs->trans($this->statuts_short[$status]), 'statut0');
  1842. } elseif ($status == 1) {
  1843. return img_picto($langs->trans($this->statuts_short[$status]), 'statut1');
  1844. } elseif ($status == 2) {
  1845. return img_picto($langs->trans($this->statuts_short[$status]), 'statut3');
  1846. } elseif ($status == 3) {
  1847. return img_picto($langs->trans($this->statuts_short[$status]), 'statut6');
  1848. } elseif ($status == 4) {
  1849. return img_picto($langs->trans($this->statuts_short[$status]), 'statut6');
  1850. } elseif ($status == 5) {
  1851. return img_picto($langs->trans($this->statuts_short[$status]), 'statut5');
  1852. }
  1853. } elseif ($mode == 4) {
  1854. if ($status == 0) {
  1855. return img_picto($langs->trans($this->statuts_short[$status]), 'statut0').' '.$langs->trans($this->statuts[$status]);
  1856. } elseif ($status == 1) {
  1857. return img_picto($langs->trans($this->statuts_short[$status]), 'statut1').' '.$langs->trans($this->statuts[$status]);
  1858. } elseif ($status == 2) {
  1859. return img_picto($langs->trans($this->statuts_short[$status]), 'statut3').' '.$langs->trans($this->statuts[$status]);
  1860. } elseif ($status == 3) {
  1861. return img_picto($langs->trans($this->statuts_short[$status]), 'statut6').' '.$langs->trans($this->statuts[$status]);
  1862. } elseif ($status == 4) {
  1863. return img_picto($langs->trans($this->statuts_short[$status]), 'statut6').' '.$langs->trans($this->statuts[$status]);
  1864. } elseif ($status == 5) {
  1865. return img_picto($langs->trans($this->statuts_short[$status]), 'statut5').' '.$langs->trans($this->statuts[$status]);
  1866. }
  1867. } elseif ($mode == 5) {
  1868. /*if ($status==0) return $langs->trans($this->statuts_short[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut0');
  1869. elseif ($status==1) return $langs->trans($this->statuts_short[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut1');
  1870. elseif ($status==2) return $langs->trans($this->statuts_short[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut3');
  1871. elseif ($status==3) return $langs->trans($this->statuts_short[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut6');
  1872. elseif ($status==4) return $langs->trans($this->statuts_short[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut6');
  1873. elseif ($status==5) return $langs->trans($this->statuts_short[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut5');
  1874. */
  1875. //else return $this->progress.' %';
  1876. return '&nbsp;';
  1877. } elseif ($mode == 6) {
  1878. /*if ($status==0) return $langs->trans($this->statuts[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut0');
  1879. elseif ($status==1) return $langs->trans($this->statuts[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut1');
  1880. elseif ($status==2) return $langs->trans($this->statuts[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut3');
  1881. elseif ($status==3) return $langs->trans($this->statuts[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut6');
  1882. elseif ($status==4) return $langs->trans($this->statuts[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut6');
  1883. elseif ($status==5) return $langs->trans($this->statuts[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut5');
  1884. */
  1885. //else return $this->progress.' %';
  1886. return '&nbsp;';
  1887. }
  1888. }
  1889. /**
  1890. * Create an intervention document on disk using template defined into PROJECT_TASK_ADDON_PDF
  1891. *
  1892. * @param string $modele force le modele a utiliser ('' par defaut)
  1893. * @param Translate $outputlangs objet lang a utiliser pour traduction
  1894. * @param int $hidedetails Hide details of lines
  1895. * @param int $hidedesc Hide description
  1896. * @param int $hideref Hide ref
  1897. * @return int 0 if KO, 1 if OK
  1898. */
  1899. public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0)
  1900. {
  1901. global $conf;
  1902. $outputlangs->load("projects");
  1903. if (!dol_strlen($modele)) {
  1904. $modele = 'nodefault';
  1905. if (!empty($this->model_pdf)) {
  1906. $modele = $this->model_pdf;
  1907. } elseif (!empty($this->modelpdf)) { // deprecated
  1908. $modele = $this->modelpdf;
  1909. } elseif (!empty($conf->global->PROJECT_TASK_ADDON_PDF)) {
  1910. $modele = $conf->global->PROJECT_TASK_ADDON_PDF;
  1911. }
  1912. }
  1913. $modelpath = "core/modules/project/task/doc/";
  1914. return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref);
  1915. }
  1916. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  1917. /**
  1918. * Load indicators for dashboard (this->nbtodo and this->nbtodolate)
  1919. *
  1920. * @param User $user Objet user
  1921. * @return WorkboardResponse|int <0 if KO, WorkboardResponse if OK
  1922. */
  1923. public function load_board($user)
  1924. {
  1925. // phpcs:enable
  1926. global $conf, $langs;
  1927. // For external user, no check is done on company because readability is managed by public status of project and assignement.
  1928. //$socid = $user->socid;
  1929. $socid = 0;
  1930. $projectstatic = new Project($this->db);
  1931. $projectsListId = $projectstatic->getProjectsAuthorizedForUser($user, 0, 1, $socid);
  1932. // List of tasks (does not care about permissions. Filtering will be done later)
  1933. $sql = "SELECT p.rowid as projectid, p.fk_statut as projectstatus,";
  1934. $sql .= " t.rowid as taskid, t.progress as progress, t.fk_statut as status,";
  1935. $sql .= " t.dateo as date_start, t.datee as datee";
  1936. $sql .= " FROM ".MAIN_DB_PREFIX."projet as p";
  1937. //$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s on p.fk_soc = s.rowid";
  1938. //if (! $user->rights->societe->client->voir && ! $socid) $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON sc.fk_soc = s.rowid";
  1939. $sql .= ", ".MAIN_DB_PREFIX."projet_task as t";
  1940. $sql .= " WHERE p.entity IN (".getEntity('project', 0).')';
  1941. $sql .= " AND p.fk_statut = 1";
  1942. $sql .= " AND t.fk_projet = p.rowid";
  1943. $sql .= " AND (t.progress IS NULL OR t.progress < 100)"; // tasks to do
  1944. if (empty($user->rights->projet->all->lire)) {
  1945. $sql .= " AND p.rowid IN (".$this->db->sanitize($projectsListId).")";
  1946. }
  1947. // No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser
  1948. //if ($socid || ! $user->rights->societe->client->voir) $sql.= " AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = ".((int) $socid).")";
  1949. // No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser
  1950. // if (! $user->rights->societe->client->voir && ! $socid) $sql.= " AND ((s.rowid = sc.fk_soc AND sc.fk_user = ".((int) $user->id).") OR (s.rowid IS NULL))";
  1951. //print $sql;
  1952. $resql = $this->db->query($sql);
  1953. if ($resql) {
  1954. $task_static = new Task($this->db);
  1955. $response = new WorkboardResponse();
  1956. $response->warning_delay = $conf->project->task->warning_delay / 60 / 60 / 24;
  1957. $response->label = $langs->trans("OpenedTasks");
  1958. if ($user->hasRight("projet", "all", "lire")) {
  1959. $response->url = DOL_URL_ROOT.'/projet/tasks/list.php?mainmenu=project';
  1960. } else {
  1961. $response->url = DOL_URL_ROOT.'/projet/tasks/list.php?mode=mine&amp;mainmenu=project';
  1962. }
  1963. $response->img = img_object('', "task");
  1964. // This assignment in condition is not a bug. It allows walking the results.
  1965. while ($obj = $this->db->fetch_object($resql)) {
  1966. $response->nbtodo++;
  1967. $task_static->projectstatus = $obj->projectstatus;
  1968. $task_static->progress = $obj->progress;
  1969. $task_static->fk_statut = $obj->status;
  1970. $task_static->date_end = $this->db->jdate($obj->datee);
  1971. if ($task_static->hasDelay()) {
  1972. $response->nbtodolate++;
  1973. }
  1974. }
  1975. return $response;
  1976. } else {
  1977. $this->error = $this->db->error();
  1978. return -1;
  1979. }
  1980. }
  1981. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  1982. /**
  1983. * Charge indicateurs this->nb de tableau de bord
  1984. *
  1985. * @return int <0 if ko, >0 if ok
  1986. */
  1987. public function load_state_board()
  1988. {
  1989. // phpcs:enable
  1990. global $user;
  1991. $mine = 0; $socid = $user->socid;
  1992. $projectstatic = new Project($this->db);
  1993. $projectsListId = $projectstatic->getProjectsAuthorizedForUser($user, $mine, 1, $socid);
  1994. // List of tasks (does not care about permissions. Filtering will be done later)
  1995. $sql = "SELECT count(p.rowid) as nb";
  1996. $sql .= " FROM ".MAIN_DB_PREFIX."projet as p";
  1997. $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s on p.fk_soc = s.rowid";
  1998. if (empty($user->rights->societe->client->voir) && !$socid) {
  1999. $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON sc.fk_soc = s.rowid";
  2000. }
  2001. $sql .= ", ".MAIN_DB_PREFIX."projet_task as t";
  2002. $sql .= " WHERE p.entity IN (".getEntity('project', 0).')';
  2003. $sql .= " AND t.fk_projet = p.rowid"; // tasks to do
  2004. if ($mine || empty($user->rights->projet->all->lire)) {
  2005. $sql .= " AND p.rowid IN (".$this->db->sanitize($projectsListId).")";
  2006. }
  2007. // No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser
  2008. //if ($socid || ! $user->rights->societe->client->voir) $sql.= " AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = ".((int) $socid).")";
  2009. if ($socid) {
  2010. $sql .= " AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = ".((int) $socid).")";
  2011. }
  2012. if (empty($user->rights->societe->client->voir) && !$socid) {
  2013. $sql .= " AND ((s.rowid = sc.fk_soc AND sc.fk_user = ".((int) $user->id).") OR (s.rowid IS NULL))";
  2014. }
  2015. $resql = $this->db->query($sql);
  2016. if ($resql) {
  2017. // This assignment in condition is not a bug. It allows walking the results.
  2018. while ($obj = $this->db->fetch_object($resql)) {
  2019. $this->nb["tasks"] = $obj->nb;
  2020. }
  2021. $this->db->free($resql);
  2022. return 1;
  2023. } else {
  2024. dol_print_error($this->db);
  2025. $this->error = $this->db->error();
  2026. return -1;
  2027. }
  2028. }
  2029. /**
  2030. * Is the task delayed?
  2031. *
  2032. * @return bool
  2033. */
  2034. public function hasDelay()
  2035. {
  2036. global $conf;
  2037. if (!($this->progress >= 0 && $this->progress < 100)) {
  2038. return false;
  2039. }
  2040. $now = dol_now();
  2041. $datetouse = ($this->date_end > 0) ? $this->date_end : ((isset($this->datee) && $this->datee > 0) ? $this->datee : 0);
  2042. return ($datetouse > 0 && ($datetouse < ($now - $conf->project->task->warning_delay)));
  2043. }
  2044. }