123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421 |
- <?php
- /* Copyright (C) 2008-2014 Laurent Destailleur <eldy@users.sourceforge.net>
- * Copyright (C) 2010-2012 Regis Houssin <regis.houssin@inodbox.com>
- * Copyright (C) 2014 Marcos García <marcosgdf@gmail.com>
- * Copyright (C) 2018-2023 Frédéric France <frederic.france@netlogic.fr>
- * Copyright (C) 2020 Juanjo Menent <jmenent@2byte.es>
- * Copyright (C) 2022 Charlene Benke <charlene@patas-monkey.com>
- * Copyright (C) 2023 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
- /**
- * \file htdocs/projet/class/task.class.php
- * \ingroup project
- * \brief This file is a CRUD class file for Task (Create/Read/Update/Delete)
- */
- require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
- require_once DOL_DOCUMENT_ROOT.'/core/class/commonobjectline.class.php';
- require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
- require_once DOL_DOCUMENT_ROOT.'/core/class/timespent.class.php';
- /**
- * Class to manage tasks
- */
- class Task extends CommonObjectLine
- {
- /**
- * @var string ID to identify managed object
- */
- public $element = 'project_task';
- /**
- * @var string Name of table without prefix where object is stored
- */
- public $table_element = 'projet_task';
- /**
- * @var string Field with ID of parent key if this field has a parent
- */
- public $fk_element = 'fk_element';
- /**
- * @var string String with name of icon for myobject.
- */
- public $picto = 'projecttask';
- /**
- * @var array List of child tables. To test if we can delete object.
- */
- protected $childtables = array(
- 'element_time' => array('name' => 'Task', 'parent' => 'projet_task', 'parentkey' => 'fk_element', 'parenttypefield' => 'elementtype', 'parenttypevalue' => 'task')
- );
- /**
- * @var int ID parent task
- */
- public $fk_task_parent = 0;
- /**
- * @var string Label of task
- */
- public $label;
- /**
- * @var string description
- */
- public $description;
- public $duration_effective; // total of time spent on this task
- public $planned_workload;
- public $date_c;
- public $date_start;
- public $date_end;
- public $progress;
- /**
- * @deprecated Use date_end instead
- */
- public $datee;
- /**
- * @var int ID
- */
- public $fk_statut;
- public $priority;
- /**
- * @var int ID
- */
- public $fk_user_creat;
- /**
- * @var int ID
- */
- public $fk_user_valid;
- public $rang;
- public $timespent_min_date;
- public $timespent_max_date;
- public $timespent_total_duration;
- public $timespent_total_amount;
- public $timespent_nblinesnull;
- public $timespent_nblines;
- // For detail of lines of timespent record, there is the property ->lines in common
- // Var used to call method addTimeSpent(). Bad practice.
- public $timespent_id;
- public $timespent_duration;
- public $timespent_old_duration;
- public $timespent_date;
- public $timespent_datehour; // More accurate start date (same than timespent_date but includes hours, minutes and seconds)
- public $timespent_withhour; // 1 = we entered also start hours for timesheet line
- public $timespent_fk_user;
- public $timespent_thm;
- public $timespent_note;
- public $timespent_fk_product;
- public $timespent_invoiceid;
- public $timespent_invoicelineid;
- public $comments = array();
- /**
- * @var array
- */
- public $statuts;
- /**
- * @var array
- */
- public $statuts_short;
- // Properties calculated from sum of llx_element_time linked to task
- public $tobill;
- public $billed;
- // Properties to store project informations
- public $projectref;
- public $projectstatus;
- public $projectlabel;
- public $opp_amount;
- public $opp_percent;
- public $fk_opp_status;
- public $usage_bill_time;
- public $public;
- public $array_options_project;
- // Properties to store thirdparty of project information
- public $socid;
- public $thirdparty_id;
- public $thirdparty_name;
- public $thirdparty_email;
- /**
- * @var float budget_amount
- */
- public $budget_amount;
- /**
- * @var float project_budget_amount
- */
- public $project_budget_amount;
- /**
- * Constructor
- *
- * @param DoliDB $db Database handler
- */
- public function __construct($db)
- {
- $this->db = $db;
- }
- /**
- * Create into database
- *
- * @param User $user User that create
- * @param int $notrigger 0=launch triggers after, 1=disable triggers
- * @return int <0 if KO, Id of created object if OK
- */
- public function create($user, $notrigger = 0)
- {
- global $conf, $langs;
- //For the date
- $now = dol_now();
- $error = 0;
- // Clean parameters
- $this->label = trim($this->label);
- $this->description = trim($this->description);
- if (!empty($this->date_start) && !empty($this->date_end) && $this->date_start > $this->date_end) {
- $this->errors[] = $langs->trans('StartDateCannotBeAfterEndDate');
- return -1;
- }
- // Check parameters
- // Put here code to add control on parameters values
- // Insert request
- $sql = "INSERT INTO ".MAIN_DB_PREFIX."projet_task (";
- $sql .= "entity";
- $sql .= ", fk_projet";
- $sql .= ", ref";
- $sql .= ", fk_task_parent";
- $sql .= ", label";
- $sql .= ", description";
- $sql .= ", datec";
- $sql .= ", fk_user_creat";
- $sql .= ", dateo";
- $sql .= ", datee";
- $sql .= ", planned_workload";
- $sql .= ", progress";
- $sql .= ", budget_amount";
- $sql .= ") VALUES (";
- $sql .= (!empty($this->entity) ? (int) $this->entity : (int) $conf->entity);
- $sql .= ", ".((int) $this->fk_project);
- $sql .= ", ".(!empty($this->ref) ? "'".$this->db->escape($this->ref)."'" : 'null');
- $sql .= ", ".((int) $this->fk_task_parent);
- $sql .= ", '".$this->db->escape($this->label)."'";
- $sql .= ", '".$this->db->escape($this->description)."'";
- $sql .= ", '".$this->db->idate($now)."'";
- $sql .= ", ".((int) $user->id);
- $sql .= ", ".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : 'null');
- $sql .= ", ".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : 'null');
- $sql .= ", ".(($this->planned_workload != '' && $this->planned_workload >= 0) ? ((int) $this->planned_workload) : 'null');
- $sql .= ", ".(($this->progress != '' && $this->progress >= 0) ? ((int) $this->progress) : 'null');
- $sql .= ", ".(($this->budget_amount != '' && $this->budget_amount >= 0) ? ((int) $this->budget_amount) : 'null');
- $sql .= ")";
- $this->db->begin();
- dol_syslog(get_class($this)."::create", LOG_DEBUG);
- $resql = $this->db->query($sql);
- if (!$resql) {
- $error++; $this->errors[] = "Error ".$this->db->lasterror();
- }
- if (!$error) {
- $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."projet_task");
- if (!$notrigger) {
- // Call trigger
- $result = $this->call_trigger('TASK_CREATE', $user);
- if ($result < 0) {
- $error++;
- }
- // End call triggers
- }
- }
- // Update extrafield
- if (!$error) {
- if (!$error) {
- $result = $this->insertExtraFields();
- if ($result < 0) {
- $error++;
- }
- }
- }
- // Commit or rollback
- if ($error) {
- foreach ($this->errors as $errmsg) {
- dol_syslog(get_class($this)."::create ".$errmsg, LOG_ERR);
- $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
- }
- $this->db->rollback();
- return -1 * $error;
- } else {
- $this->db->commit();
- return $this->id;
- }
- }
- /**
- * Load object in memory from database
- *
- * @param int $id Id object
- * @param int $ref ref object
- * @param int $loadparentdata Also load parent data
- * @return int <0 if KO, 0 if not found, >0 if OK
- */
- public function fetch($id, $ref = '', $loadparentdata = 0)
- {
- global $langs, $conf;
- $sql = "SELECT";
- $sql .= " t.rowid,";
- $sql .= " t.ref,";
- $sql .= " t.entity,";
- $sql .= " t.fk_projet as fk_project,";
- $sql .= " t.fk_task_parent,";
- $sql .= " t.label,";
- $sql .= " t.description,";
- $sql .= " t.duration_effective,";
- $sql .= " t.planned_workload,";
- $sql .= " t.datec,";
- $sql .= " t.dateo,";
- $sql .= " t.datee,";
- $sql .= " t.fk_user_creat,";
- $sql .= " t.fk_user_valid,";
- $sql .= " t.fk_statut,";
- $sql .= " t.progress,";
- $sql .= " t.budget_amount,";
- $sql .= " t.priority,";
- $sql .= " t.note_private,";
- $sql .= " t.note_public,";
- $sql .= " t.rang";
- if (!empty($loadparentdata)) {
- $sql .= ", t2.ref as task_parent_ref";
- $sql .= ", t2.rang as task_parent_position";
- }
- $sql .= " FROM ".MAIN_DB_PREFIX."projet_task as t";
- if (!empty($loadparentdata)) {
- $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task as t2 ON t.fk_task_parent = t2.rowid";
- }
- $sql .= " WHERE ";
- if (!empty($ref)) {
- $sql .= "entity IN (".getEntity('project').")";
- $sql .= " AND t.ref = '".$this->db->escape($ref)."'";
- } else {
- $sql .= "t.rowid = ".((int) $id);
- }
- dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
- $resql = $this->db->query($sql);
- if ($resql) {
- $num_rows = $this->db->num_rows($resql);
- if ($num_rows) {
- $obj = $this->db->fetch_object($resql);
- $this->id = $obj->rowid;
- $this->ref = $obj->ref;
- $this->entity = $obj->entity;
- $this->fk_project = $obj->fk_project;
- $this->fk_task_parent = $obj->fk_task_parent;
- $this->label = $obj->label;
- $this->description = $obj->description;
- $this->duration_effective = $obj->duration_effective;
- $this->planned_workload = $obj->planned_workload;
- $this->date_c = $this->db->jdate($obj->datec);
- $this->date_start = $this->db->jdate($obj->dateo);
- $this->date_end = $this->db->jdate($obj->datee);
- $this->fk_user_creat = $obj->fk_user_creat;
- $this->fk_user_valid = $obj->fk_user_valid;
- $this->fk_statut = $obj->fk_statut;
- $this->progress = $obj->progress;
- $this->budget_amount = $obj->budget_amount;
- $this->priority = $obj->priority;
- $this->note_private = $obj->note_private;
- $this->note_public = $obj->note_public;
- $this->rang = $obj->rang;
- if (!empty($loadparentdata)) {
- $this->task_parent_ref = $obj->task_parent_ref;
- $this->task_parent_position = $obj->task_parent_position;
- }
- // Retrieve all extrafield
- $this->fetch_optionals();
- }
- $this->db->free($resql);
- if ($num_rows) {
- return 1;
- } else {
- return 0;
- }
- } else {
- $this->error = "Error ".$this->db->lasterror();
- return -1;
- }
- }
- /**
- * Update database
- *
- * @param User $user User that modify
- * @param int $notrigger 0=launch triggers after, 1=disable triggers
- * @return int <=0 if KO, >0 if OK
- */
- public function update($user = null, $notrigger = 0)
- {
- global $conf, $langs;
- $error = 0;
- // Clean parameters
- if (isset($this->fk_project)) {
- $this->fk_project = trim($this->fk_project);
- }
- if (isset($this->ref)) {
- $this->ref = trim($this->ref);
- }
- if (isset($this->fk_task_parent)) {
- $this->fk_task_parent = (int) $this->fk_task_parent;
- }
- if (isset($this->label)) {
- $this->label = trim($this->label);
- }
- if (isset($this->description)) {
- $this->description = trim($this->description);
- }
- if (isset($this->duration_effective)) {
- $this->duration_effective = trim($this->duration_effective);
- }
- if (isset($this->planned_workload)) {
- $this->planned_workload = trim($this->planned_workload);
- }
- if (isset($this->budget_amount)) {
- $this->budget_amount = trim($this->budget_amount);
- }
- if (!empty($this->date_start) && !empty($this->date_end) && $this->date_start > $this->date_end) {
- $this->errors[] = $langs->trans('StartDateCannotBeAfterEndDate');
- return -1;
- }
- // Check parameters
- // Put here code to add control on parameters values
- // Update request
- $sql = "UPDATE ".MAIN_DB_PREFIX."projet_task SET";
- $sql .= " fk_projet=".(isset($this->fk_project) ? $this->fk_project : "null").",";
- $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "'".$this->db->escape($this->id)."'").",";
- $sql .= " fk_task_parent=".(isset($this->fk_task_parent) ? $this->fk_task_parent : "null").",";
- $sql .= " label=".(isset($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
- $sql .= " description=".(isset($this->description) ? "'".$this->db->escape($this->description)."'" : "null").",";
- $sql .= " duration_effective=".(isset($this->duration_effective) ? $this->duration_effective : "null").",";
- $sql .= " planned_workload=".((isset($this->planned_workload) && $this->planned_workload != '') ? $this->planned_workload : "null").",";
- $sql .= " dateo=".($this->date_start != '' ? "'".$this->db->idate($this->date_start)."'" : 'null').",";
- $sql .= " datee=".($this->date_end != '' ? "'".$this->db->idate($this->date_end)."'" : 'null').",";
- $sql .= " progress=".(($this->progress != '' && $this->progress >= 0) ? $this->progress : 'null').",";
- $sql .= " budget_amount=".(($this->budget_amount != '' && $this->budget_amount >= 0) ? $this->budget_amount : 'null').",";
- $sql .= " rang=".((!empty($this->rang)) ? $this->rang : "0");
- $sql .= " WHERE rowid=".((int) $this->id);
- $this->db->begin();
- dol_syslog(get_class($this)."::update", LOG_DEBUG);
- $resql = $this->db->query($sql);
- if (!$resql) {
- $error++; $this->errors[] = "Error ".$this->db->lasterror();
- }
- // Update extrafield
- if (!$error) {
- $result = $this->insertExtraFields();
- if ($result < 0) {
- $error++;
- }
- }
- if (!$error && !empty($conf->global->PROJECT_CLASSIFY_CLOSED_WHEN_ALL_TASKS_DONE)) {
- // Close the parent project if it is open (validated) and its tasks are 100% completed
- $project = new Project($this->db);
- if ($project->fetch($this->fk_project) > 0) {
- if ($project->statut == Project::STATUS_VALIDATED) {
- $project->getLinesArray(null); // this method does not return <= 0 if fails
- $projectCompleted = array_reduce(
- $project->lines,
- function ($allTasksCompleted, $task) {
- return $allTasksCompleted && $task->progress >= 100;
- },
- 1
- );
- if ($projectCompleted) {
- if ($project->setClose($user) <= 0) {
- $error++;
- }
- }
- }
- } else {
- $error++;
- }
- if ($error) {
- $this->errors[] = $project->error;
- }
- }
- if (!$error) {
- if (!$notrigger) {
- // Call trigger
- $result = $this->call_trigger('TASK_MODIFY', $user);
- if ($result < 0) {
- $error++;
- }
- // End call triggers
- }
- }
- if (!$error && (is_object($this->oldcopy) && $this->oldcopy->ref !== $this->ref)) {
- // We remove directory
- if ($conf->project->dir_output) {
- $project = new Project($this->db);
- $project->fetch($this->fk_project);
- $olddir = $conf->project->dir_output.'/'.dol_sanitizeFileName($project->ref).'/'.dol_sanitizeFileName($this->oldcopy->ref);
- $newdir = $conf->project->dir_output.'/'.dol_sanitizeFileName($project->ref).'/'.dol_sanitizeFileName($this->ref);
- if (file_exists($olddir)) {
- include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
- $res = dol_move_dir($olddir, $newdir);
- if (!$res) {
- $langs->load("errors");
- $this->error = $langs->trans('ErrorFailToRenameDir', $olddir, $newdir);
- $error++;
- }
- }
- }
- }
- // Commit or rollback
- if ($error) {
- foreach ($this->errors as $errmsg) {
- dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
- $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
- }
- $this->db->rollback();
- return -1 * $error;
- } else {
- $this->db->commit();
- return 1;
- }
- }
- /**
- * Delete task from database
- *
- * @param User $user User that delete
- * @param int $notrigger 0=launch triggers after, 1=disable triggers
- * @return int <0 if KO, >0 if OK
- */
- public function delete($user, $notrigger = 0)
- {
- global $conf, $langs;
- require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
- $error = 0;
- $this->db->begin();
- if ($this->hasChildren() > 0) {
- dol_syslog(get_class($this)."::delete Can't delete record as it has some sub tasks", LOG_WARNING);
- $this->error = 'ErrorRecordHasSubTasks';
- $this->db->rollback();
- return 0;
- }
- $objectisused = $this->isObjectUsed($this->id);
- if (!empty($objectisused)) {
- dol_syslog(get_class($this)."::delete Can't delete record as it has some child", LOG_WARNING);
- $this->error = 'ErrorRecordHasChildren';
- $this->db->rollback();
- return 0;
- }
- if (!$error) {
- // Delete linked contacts
- $res = $this->delete_linked_contact();
- if ($res < 0) {
- $this->error = 'ErrorFailToDeleteLinkedContact';
- //$error++;
- $this->db->rollback();
- return 0;
- }
- }
- if (!$error) {
- $sql = "DELETE FROM ".MAIN_DB_PREFIX."element_time";
- $sql .= " WHERE fk_element = ".((int) $this->id)." AND elementtype = 'task'";
- $resql = $this->db->query($sql);
- if (!$resql) {
- $error++; $this->errors[] = "Error ".$this->db->lasterror();
- }
- }
- if (!$error) {
- $sql = "DELETE FROM ".MAIN_DB_PREFIX."projet_task_extrafields";
- $sql .= " WHERE fk_object = ".((int) $this->id);
- $resql = $this->db->query($sql);
- if (!$resql) {
- $error++; $this->errors[] = "Error ".$this->db->lasterror();
- }
- }
- if (!$error) {
- $sql = "DELETE FROM ".MAIN_DB_PREFIX."projet_task";
- $sql .= " WHERE rowid=".((int) $this->id);
- $resql = $this->db->query($sql);
- if (!$resql) {
- $error++; $this->errors[] = "Error ".$this->db->lasterror();
- }
- }
- if (!$error) {
- if (!$notrigger) {
- // Call trigger
- $result = $this->call_trigger('TASK_DELETE', $user);
- if ($result < 0) {
- $error++;
- }
- // End call triggers
- }
- }
- // Commit or rollback
- if ($error) {
- foreach ($this->errors as $errmsg) {
- dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
- $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
- }
- $this->db->rollback();
- return -1 * $error;
- } else {
- //Delete associated link file
- if ($conf->project->dir_output) {
- $projectstatic = new Project($this->db);
- $projectstatic->fetch($this->fk_project);
- $dir = $conf->project->dir_output."/".dol_sanitizeFileName($projectstatic->ref).'/'.dol_sanitizeFileName($this->id);
- dol_syslog(get_class($this)."::delete dir=".$dir, LOG_DEBUG);
- if (file_exists($dir)) {
- require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
- $res = @dol_delete_dir_recursive($dir);
- if (!$res) {
- $this->error = 'ErrorFailToDeleteDir';
- $this->db->rollback();
- return 0;
- }
- }
- }
- $this->db->commit();
- return 1;
- }
- }
- /**
- * Return nb of children
- *
- * @return int <0 if KO, 0 if no children, >0 if OK
- */
- public function hasChildren()
- {
- $error = 0;
- $ret = 0;
- $sql = "SELECT COUNT(*) as nb";
- $sql .= " FROM ".MAIN_DB_PREFIX."projet_task";
- $sql .= " WHERE fk_task_parent = ".((int) $this->id);
- dol_syslog(get_class($this)."::hasChildren", LOG_DEBUG);
- $resql = $this->db->query($sql);
- if (!$resql) {
- $error++; $this->errors[] = "Error ".$this->db->lasterror();
- } else {
- $obj = $this->db->fetch_object($resql);
- if ($obj) {
- $ret = $obj->nb;
- }
- $this->db->free($resql);
- }
- if (!$error) {
- return $ret;
- } else {
- return -1;
- }
- }
- /**
- * Return nb of time spent
- *
- * @return int <0 if KO, 0 if no children, >0 if OK
- */
- public function hasTimeSpent()
- {
- $error = 0;
- $ret = 0;
- $sql = "SELECT COUNT(*) as nb";
- $sql .= " FROM ".MAIN_DB_PREFIX."element_time";
- $sql .= " WHERE fk_element = ".((int) $this->id);
- $sql .= " AND elementtype = 'task'";
- dol_syslog(get_class($this)."::hasTimeSpent", LOG_DEBUG);
- $resql = $this->db->query($sql);
- if (!$resql) {
- $error++; $this->errors[] = "Error ".$this->db->lasterror();
- } else {
- $obj = $this->db->fetch_object($resql);
- if ($obj) {
- $ret = $obj->nb;
- }
- $this->db->free($resql);
- }
- if (!$error) {
- return $ret;
- } else {
- return -1;
- }
- }
- /**
- * getTooltipContentArray
- *
- * @param array $params ex option, infologin
- * @since v18
- * @return array
- */
- public function getTooltipContentArray($params)
- {
- global $langs;
- $langs->load('projects');
- $datas = [];
- $datas['picto'] = img_picto('', $this->picto).' <u>'.$langs->trans("Task").'</u>';
- if (!empty($this->ref)) {
- $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
- }
- if (!empty($this->label)) {
- $datas['label'] = '<br><b>'.$langs->trans('LabelTask').':</b> '.$this->label;
- }
- if ($this->date_start || $this->date_end) {
- $datas['range'] = "<br>".get_date_range($this->date_start, $this->date_end, '', $langs, 0);
- }
- return $datas;
- }
- /**
- * Return clicable name (with picto eventually)
- *
- * @param int $withpicto 0=No picto, 1=Include picto into link, 2=Only picto
- * @param string $option 'withproject' or ''
- * @param string $mode Mode 'task', 'time', 'contact', 'note', document' define page to link to.
- * @param int $addlabel 0=Default, 1=Add label into string, >1=Add first chars into string
- * @param string $sep Separator between ref and label if option addlabel is set
- * @param int $notooltip 1=Disable tooltip
- * @param int $save_lastsearch_value -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking
- * @return string Chaine avec URL
- */
- public function getNomUrl($withpicto = 0, $option = '', $mode = 'task', $addlabel = 0, $sep = ' - ', $notooltip = 0, $save_lastsearch_value = -1)
- {
- global $conf, $langs, $user;
- if (!empty($conf->dol_no_mouse_hover)) {
- $notooltip = 1; // Force disable tooltips
- }
- $result = '';
- $params = [
- 'id' => $this->id,
- 'objecttype' => $this->element,
- ];
- $classfortooltip = 'classfortooltip';
- $dataparams = '';
- if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
- $classfortooltip = 'classforajaxtooltip';
- $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
- $label = '';
- } else {
- $label = implode($this->getTooltipContentArray($params));
- }
- $url = DOL_URL_ROOT.'/projet/tasks/'.$mode.'.php?id='.$this->id.($option == 'withproject' ? '&withproject=1' : '');
- // Add param to save lastsearch_values or not
- $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
- if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
- $add_save_lastsearch_values = 1;
- }
- if ($add_save_lastsearch_values) {
- $url .= '&save_lastsearch_values=1';
- }
- $linkclose = '';
- if (empty($notooltip)) {
- if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
- $label = $langs->trans("ShowTask");
- $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
- }
- $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
- $linkclose .= $dataparams.' class="'.$classfortooltip.' nowraponall"';
- } else {
- $linkclose .= ' class="nowraponall"';
- }
- $linkstart = '<a href="'.$url.'"';
- $linkstart .= $linkclose.'>';
- $linkend = '</a>';
- $picto = 'projecttask';
- $result .= $linkstart;
- if ($withpicto) {
- $result .= img_object(($notooltip ? '' : $label), $picto, 'class="paddingright"', 0, 0, $notooltip ? 0 : 1);
- }
- if ($withpicto != 2) {
- $result .= $this->ref;
- }
- $result .= $linkend;
- if ($withpicto != 2) {
- $result .= (($addlabel && $this->label) ? $sep.dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
- }
- return $result;
- }
- /**
- * Initialise an instance with random values.
- * Used to build previews or test instances.
- * id must be 0 if object instance is a specimen.
- *
- * @return void
- */
- public function initAsSpecimen()
- {
- $this->id = 0;
- $this->fk_project = '';
- $this->ref = 'TK01';
- $this->fk_task_parent = null;
- $this->label = 'Specimen task TK01';
- $this->duration_effective = '';
- $this->fk_user_creat = null;
- $this->progress = '25';
- $this->fk_statut = null;
- $this->note = 'This is a specimen task not';
- }
- /**
- * Return list of tasks for all projects or for one particular project
- * Sort order is on project, then on position of task, and last on start date of first level task
- *
- * @param User $usert Object user to limit tasks affected to a particular user
- * @param User $userp Object user to limit projects of a particular user and public projects
- * @param int $projectid Project id
- * @param int $socid Third party id
- * @param int $mode 0=Return list of tasks and their projects, 1=Return projects and tasks if exists
- * @param string $filteronproj Filter on project ref or label
- * @param string $filteronprojstatus Filter on project status ('-1'=no filter, '0,1'=Draft+Validated only)
- * @param string $morewherefilter Add more filter into where SQL request (must start with ' AND ...')
- * @param string $filteronprojuser Filter on user that is a contact of project
- * @param string $filterontaskuser Filter on user assigned to task
- * @param Extrafields $extrafields Show additional column from project or task
- * @param int $includebilltime Calculate also the time to bill and billed
- * @param array $search_array_options Array of search filters. Not Used yet.
- * @param int $loadextras Fetch all Extrafields on each project and task
- * @param int $loadRoleMode 1= will test Roles on task; 0 used in delete project action
- * @param string $sortfield Sort field
- * @param string $sortorder Sort order
- * @return array|string Array of tasks
- */
- 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 = '')
- {
- global $conf, $hookmanager;
- $tasks = array();
- //print $usert.'-'.$userp.'-'.$projectid.'-'.$socid.'-'.$mode.'<br>';
- // List of tasks (does not care about permissions. Filtering will be done later)
- $sql = "SELECT ";
- if ($filteronprojuser > 0 || $filterontaskuser > 0) {
- $sql .= " DISTINCT"; // We may get several time the same record if user has several roles on same project/task
- }
- $sql .= " p.rowid as projectid, p.ref, p.title as plabel, p.public, p.fk_statut as projectstatus, p.usage_bill_time,";
- $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,";
- $sql .= " t.dateo as date_start, t.datee as date_end, t.planned_workload, t.rang,";
- $sql .= " t.description, ";
- $sql .= " t.budget_amount, ";
- $sql .= " s.rowid as thirdparty_id, s.nom as thirdparty_name, s.email as thirdparty_email,";
- $sql .= " p.fk_opp_status, p.opp_amount, p.opp_percent, p.budget_amount as project_budget_amount";
- if ($loadextras) { // TODO Replace this with a fetch_optionnal() on the project after the fetch_object of line.
- if (!empty($extrafields->attributes['projet']['label'])) {
- foreach ($extrafields->attributes['projet']['label'] as $key => $val) {
- $sql .= ($extrafields->attributes['projet']['type'][$key] != 'separate' ? ",efp.".$key." as options_".$key : '');
- }
- }
- }
- if ($includebilltime) {
- $sql .= ", SUM(tt.element_duration * ".$this->db->ifsql("invoice_id IS NULL", "1", "0").") as tobill, SUM(tt.element_duration * ".$this->db->ifsql("invoice_id IS NULL", "0", "1").") as billed";
- }
- $sql .= " FROM ".MAIN_DB_PREFIX."projet as p";
- $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
- if ($loadextras) {
- $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_extrafields as efp ON (p.rowid = efp.fk_object)";
- }
- if ($mode == 0) {
- if ($filteronprojuser > 0) {
- $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec";
- $sql .= ", ".MAIN_DB_PREFIX."c_type_contact as ctc";
- }
- $sql .= ", ".MAIN_DB_PREFIX."projet_task as t";
- if ($includebilltime) {
- $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."element_time as tt ON (tt.fk_element = t.rowid AND tt.elementtype='task')";
- }
- if ($filterontaskuser > 0) {
- $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec2";
- $sql .= ", ".MAIN_DB_PREFIX."c_type_contact as ctc2";
- }
- $sql .= " WHERE p.entity IN (".getEntity('project').")";
- $sql .= " AND t.fk_projet = p.rowid";
- } elseif ($mode == 1) {
- if ($filteronprojuser > 0) {
- $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec";
- $sql .= ", ".MAIN_DB_PREFIX."c_type_contact as ctc";
- }
- if ($filterontaskuser > 0) {
- $sql .= ", ".MAIN_DB_PREFIX."projet_task as t";
- if ($includebilltime) {
- $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."element_time as tt ON (tt.fk_element = t.rowid AND tt.elementtype='task')";
- }
- $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec2";
- $sql .= ", ".MAIN_DB_PREFIX."c_type_contact as ctc2";
- } else {
- $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task as t on t.fk_projet = p.rowid";
- if ($includebilltime) {
- $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."element_time as tt ON (tt.fk_element = t.rowid AND tt.elementtype = 'task')";
- }
- }
- $sql .= " WHERE p.entity IN (".getEntity('project').")";
- } else {
- return 'BadValueForParameterMode';
- }
- if ($filteronprojuser > 0) {
- $sql .= " AND p.rowid = ec.element_id";
- $sql .= " AND ctc.rowid = ec.fk_c_type_contact";
- $sql .= " AND ctc.element = 'project'";
- $sql .= " AND ec.fk_socpeople = ".((int) $filteronprojuser);
- $sql .= " AND ec.statut = 4";
- $sql .= " AND ctc.source = 'internal'";
- }
- if ($filterontaskuser > 0) {
- $sql .= " AND t.fk_projet = p.rowid";
- $sql .= " AND p.rowid = ec2.element_id";
- $sql .= " AND ctc2.rowid = ec2.fk_c_type_contact";
- $sql .= " AND ctc2.element = 'project_task'";
- $sql .= " AND ec2.fk_socpeople = ".((int) $filterontaskuser);
- $sql .= " AND ec2.statut = 4";
- $sql .= " AND ctc2.source = 'internal'";
- }
- if ($socid) {
- $sql .= " AND p.fk_soc = ".((int) $socid);
- }
- if ($projectid) {
- $sql .= " AND p.rowid IN (".$this->db->sanitize($projectid).")";
- }
- if ($filteronproj) {
- $sql .= natural_search(array("p.ref", "p.title"), $filteronproj);
- }
- if ($filteronprojstatus && $filteronprojstatus != '-1') {
- $sql .= " AND p.fk_statut IN (".$this->db->sanitize($filteronprojstatus).")";
- }
- if ($morewherefilter) {
- $sql .= $morewherefilter;
- }
- // Add where from extra fields
- $extrafieldsobjectkey = 'projet_task';
- $extrafieldsobjectprefix = 'efpt.';
- global $db; // needed for extrafields_list_search_sql.tpl
- include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_sql.tpl.php';
- // Add where from hooks
- $parameters = array();
- $reshook = $hookmanager->executeHooks('printFieldListWhere', $parameters); // Note that $action and $object may have been modified by hook
- $sql .= $hookmanager->resPrint;
- if ($includebilltime) {
- $sql .= " GROUP BY p.rowid, p.ref, p.title, p.public, p.fk_statut, p.usage_bill_time,";
- $sql .= " t.datec, t.dateo, t.datee, t.tms,";
- $sql .= " t.rowid, t.ref, t.label, t.description, t.fk_task_parent, t.duration_effective, t.progress, t.fk_statut,";
- $sql .= " t.dateo, t.datee, t.planned_workload, t.rang,";
- $sql .= " t.description, ";
- $sql .= " t.budget_amount, ";
- $sql .= " s.rowid, s.nom, s.email,";
- $sql .= " p.fk_opp_status, p.opp_amount, p.opp_percent, p.budget_amount";
- if ($loadextras) {
- if (!empty($extrafields->attributes['projet']['label'])) {
- foreach ($extrafields->attributes['projet']['label'] as $key => $val) {
- $sql .= ($extrafields->attributes['projet']['type'][$key] != 'separate' ? ",efp.".$key : '');
- }
- }
- }
- }
- if ($sortfield && $sortorder) {
- $sql .= $this->db->order($sortfield, $sortorder);
- } else {
- $sql .= " ORDER BY p.ref, t.rang, t.dateo";
- }
- //print $sql;exit;
- dol_syslog(get_class($this)."::getTasksArray", LOG_DEBUG);
- $resql = $this->db->query($sql);
- if ($resql) {
- $num = $this->db->num_rows($resql);
- $i = 0;
- // Loop on each record found, so each couple (project id, task id)
- while ($i < $num) {
- $error = 0;
- $obj = $this->db->fetch_object($resql);
- if ($loadRoleMode) {
- if ((!$obj->public) && (is_object($userp))) { // If not public project and we ask a filter on project owned by a user
- if (!$this->getUserRolesForProjectsOrTasks($userp, null, $obj->projectid, 0)) {
- $error++;
- }
- }
- if (is_object($usert)) { // If we ask a filter on a user affected to a task
- if (!$this->getUserRolesForProjectsOrTasks(null, $usert, $obj->projectid, $obj->taskid)) {
- $error++;
- }
- }
- }
- if (!$error) {
- $tasks[$i] = new Task($this->db);
- $tasks[$i]->id = $obj->taskid;
- $tasks[$i]->ref = $obj->taskref;
- $tasks[$i]->fk_project = $obj->projectid;
- // Data from project
- $tasks[$i]->projectref = $obj->ref;
- $tasks[$i]->projectlabel = $obj->plabel;
- $tasks[$i]->projectstatus = $obj->projectstatus;
- $tasks[$i]->fk_opp_status = $obj->fk_opp_status;
- $tasks[$i]->opp_amount = $obj->opp_amount;
- $tasks[$i]->opp_percent = $obj->opp_percent;
- $tasks[$i]->budget_amount = $obj->budget_amount;
- $tasks[$i]->project_budget_amount = $obj->project_budget_amount;
- $tasks[$i]->usage_bill_time = $obj->usage_bill_time;
- $tasks[$i]->label = $obj->label;
- $tasks[$i]->description = $obj->description;
- $tasks[$i]->fk_task_parent = $obj->fk_task_parent;
- $tasks[$i]->duration_effective = $obj->duration_effective;
- $tasks[$i]->planned_workload = $obj->planned_workload;
- if ($includebilltime) {
- // Data summed from element_time linked to task
- $tasks[$i]->tobill = $obj->tobill;
- $tasks[$i]->billed = $obj->billed;
- }
- $tasks[$i]->progress = $obj->progress;
- $tasks[$i]->fk_statut = $obj->status;
- $tasks[$i]->public = $obj->public;
- $tasks[$i]->date_start = $this->db->jdate($obj->date_start);
- $tasks[$i]->date_end = $this->db->jdate($obj->date_end);
- $tasks[$i]->rang = $obj->rang;
- $tasks[$i]->socid = $obj->thirdparty_id; // For backward compatibility
- $tasks[$i]->thirdparty_id = $obj->thirdparty_id;
- $tasks[$i]->thirdparty_name = $obj->thirdparty_name;
- $tasks[$i]->thirdparty_email = $obj->thirdparty_email;
- if ($loadextras) {
- if (!empty($extrafields->attributes['projet']['label'])) {
- foreach ($extrafields->attributes['projet']['label'] as $key => $val) {
- if ($extrafields->attributes['projet']['type'][$key] != 'separate') {
- $tmpvar = 'options_'.$key;
- $tasks[$i]->array_options_project['options_'.$key] = $obj->$tmpvar;
- }
- }
- }
- }
- /* Removed, already included into the $tasks[$i]->fetch_optionals(); just after
- if (!empty($extrafields->attributes['projet_task']['label'])) {
- foreach ($extrafields->attributes['projet_task']['label'] as $key => $val) {
- if ($extrafields->attributes['projet_task']['type'][$key] != 'separate') {
- $tmpvar = 'options_'.$key;
- $tasks[$i]->array_options['options_'.$key] = $obj->$tmpvar;
- }
- }
- }
- */
- if ($loadextras) {
- $tasks[$i]->fetch_optionals();
- }
- }
- $i++;
- }
- $this->db->free($resql);
- } else {
- dol_print_error($this->db);
- }
- return $tasks;
- }
- /**
- * Return list of roles for a user for each projects or each tasks (or a particular project or a particular task).
- *
- * @param User|null $userp Return roles on project for this internal user. If set, usert and taskid must not be defined.
- * @param User|null $usert Return roles on task for this internal user. If set userp must NOT be defined. -1 means no filter.
- * @param int $projectid Project id list separated with , to filter on project
- * @param int $taskid Task id to filter on a task
- * @param integer $filteronprojstatus Filter on project status if userp is set. Not used if userp not defined.
- * @return array|int Array (projectid => 'list of roles for project' or taskid => 'list of roles for task')
- */
- public function getUserRolesForProjectsOrTasks($userp, $usert, $projectid = '', $taskid = 0, $filteronprojstatus = -1)
- {
- $arrayroles = array();
- dol_syslog(get_class($this)."::getUserRolesForProjectsOrTasks userp=".is_object($userp)." usert=".is_object($usert)." projectid=".$projectid." taskid=".$taskid);
- // We want role of user for a projet or role of user for a task. Both are not possible.
- if (empty($userp) && empty($usert)) {
- $this->error = "CallWithWrongParameters";
- return -1;
- }
- if (!empty($userp) && !empty($usert)) {
- $this->error = "CallWithWrongParameters";
- return -1;
- }
- /* Liste des taches et role sur les projets ou taches */
- $sql = "SELECT pt.rowid as pid, ec.element_id, ctc.code, ctc.source";
- if ($userp) {
- $sql .= " FROM ".MAIN_DB_PREFIX."projet as pt";
- }
- if ($usert && $filteronprojstatus > -1) {
- $sql .= " FROM ".MAIN_DB_PREFIX."projet as p, ".MAIN_DB_PREFIX."projet_task as pt";
- }
- if ($usert && $filteronprojstatus <= -1) {
- $sql .= " FROM ".MAIN_DB_PREFIX."projet_task as pt";
- }
- $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec";
- $sql .= ", ".MAIN_DB_PREFIX."c_type_contact as ctc";
- $sql .= " WHERE pt.rowid = ec.element_id";
- if ($userp && $filteronprojstatus > -1) {
- $sql .= " AND pt.fk_statut = ".((int) $filteronprojstatus);
- }
- if ($usert && $filteronprojstatus > -1) {
- $sql .= " AND pt.fk_projet = p.rowid AND p.fk_statut = ".((int) $filteronprojstatus);
- }
- if ($userp) {
- $sql .= " AND ctc.element = 'project'";
- }
- if ($usert) {
- $sql .= " AND ctc.element = 'project_task'";
- }
- $sql .= " AND ctc.rowid = ec.fk_c_type_contact";
- if ($userp) {
- $sql .= " AND ec.fk_socpeople = ".((int) $userp->id);
- }
- if ($usert) {
- $sql .= " AND ec.fk_socpeople = ".((int) $usert->id);
- }
- $sql .= " AND ec.statut = 4";
- $sql .= " AND ctc.source = 'internal'";
- if ($projectid) {
- if ($userp) {
- $sql .= " AND pt.rowid IN (".$this->db->sanitize($projectid).")";
- }
- if ($usert) {
- $sql .= " AND pt.fk_projet IN (".$this->db->sanitize($projectid).")";
- }
- }
- if ($taskid) {
- if ($userp) {
- $sql .= " ERROR SHOULD NOT HAPPENS";
- }
- if ($usert) {
- $sql .= " AND pt.rowid = ".((int) $taskid);
- }
- }
- //print $sql;
- dol_syslog(get_class($this)."::getUserRolesForProjectsOrTasks execute request", LOG_DEBUG);
- $resql = $this->db->query($sql);
- if ($resql) {
- $num = $this->db->num_rows($resql);
- $i = 0;
- while ($i < $num) {
- $obj = $this->db->fetch_object($resql);
- if (empty($arrayroles[$obj->pid])) {
- $arrayroles[$obj->pid] = $obj->code;
- } else {
- $arrayroles[$obj->pid] .= ','.$obj->code;
- }
- $i++;
- }
- $this->db->free($resql);
- } else {
- dol_print_error($this->db);
- }
- return $arrayroles;
- }
- /**
- * Return list of id of contacts of task
- *
- * @param string $source Source
- * @return array Array of id of contacts
- */
- public function getListContactId($source = 'internal')
- {
- $contactAlreadySelected = array();
- $tab = $this->liste_contact(-1, $source);
- //var_dump($tab);
- $num = count($tab);
- $i = 0;
- while ($i < $num) {
- if ($source == 'thirdparty') {
- $contactAlreadySelected[$i] = $tab[$i]['socid'];
- } else {
- $contactAlreadySelected[$i] = $tab[$i]['id'];
- }
- $i++;
- }
- return $contactAlreadySelected;
- }
- /**
- * Add time spent
- *
- * @param User $user User object
- * @param int $notrigger 0=launch triggers after, 1=disable triggers
- * @return int <=0 if KO, >0 if OK
- */
- public function addTimeSpent($user, $notrigger = 0)
- {
- global $conf, $langs;
- dol_syslog(get_class($this)."::addTimeSpent", LOG_DEBUG);
- $ret = 0;
- $now = dol_now();
- // Check parameters
- if (!is_object($user)) {
- dol_print_error('', "Method addTimeSpent was called with wrong parameter user");
- return -1;
- }
- // Clean parameters
- if (isset($this->timespent_note)) {
- $this->timespent_note = trim($this->timespent_note);
- }
- if (empty($this->timespent_datehour) || ($this->timespent_date != $this->timespent_datehour)) {
- $this->timespent_datehour = $this->timespent_date;
- }
- if (!empty($conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS)) {
- require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
- $restrictBefore = dol_time_plus_duree(dol_now(), - $conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS, 'm');
- if ($this->timespent_date < $restrictBefore) {
- $this->error = $langs->trans('TimeRecordingRestrictedToNMonthsBack', $conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS);
- $this->errors[] = $this->error;
- return -1;
- }
- }
- $this->db->begin();
- $timespent = new TimeSpent($this->db);
- $timespent->fk_element = $this->id;
- $timespent->elementtype = 'task';
- $timespent->element_date = $this->timespent_date;
- $timespent->element_datehour = $this->timespent_datehour;
- $timespent->element_date_withhour = $this->timespent_withhour;
- $timespent->element_duration = $this->timespent_duration;
- $timespent->fk_user = $this->timespent_fk_user;
- $timespent->fk_product = $this->timespent_fk_product;
- $timespent->note = $this->timespent_note;
- $timespent->datec = $this->db->idate($now);
- $result = $timespent->create($user);
- if ($result > 0) {
- $ret = $result;
- $this->timespent_id = $result;
- if (!$notrigger) {
- // Call trigger
- $result = $this->call_trigger('TASK_TIMESPENT_CREATE', $user);
- if ($result < 0) {
- $ret = -1;
- }
- // End call triggers
- }
- } else {
- $this->error = $this->db->lasterror();
- $ret = -1;
- }
- if ($ret > 0) {
- // Recalculate amount of time spent for task and update denormalized field
- $sql = "UPDATE ".MAIN_DB_PREFIX."projet_task";
- $sql .= " SET duration_effective = (SELECT SUM(element_duration) FROM ".MAIN_DB_PREFIX."element_time as ptt where ptt.elementtype = 'task' AND ptt.fk_element = ".((int) $this->id).")";
- if (isset($this->progress)) {
- $sql .= ", progress = ".((float) $this->progress); // Do not overwrite value if not provided
- }
- $sql .= " WHERE rowid = ".((int) $this->id);
- dol_syslog(get_class($this)."::addTimeSpent", LOG_DEBUG);
- if (!$this->db->query($sql)) {
- $this->error = $this->db->lasterror();
- $ret = -2;
- }
- // Update hourly rate of this time spent entry
- $resql_thm_user = $this->db->query("SELECT thm FROM " . MAIN_DB_PREFIX . "user WHERE rowid = " . ((int) $timespent->fk_user));
- if (!empty($resql_thm_user)) {
- $obj_thm_user = $this->db->fetch_object($resql_thm_user);
- $timespent->thm = $obj_thm_user->thm;
- }
- $res_update = $timespent->update($user);
- dol_syslog(get_class($this)."::addTimeSpent", LOG_DEBUG);
- if ($res_update <= 0) {
- $this->error = $this->db->lasterror();
- $ret = -2;
- }
- }
- if ($ret > 0) {
- $this->db->commit();
- } else {
- $this->db->rollback();
- }
- return $ret;
- }
- /**
- * Fetch records of time spent of this task
- *
- * @param string $morewherefilter Add more filter into where SQL request (must start with ' AND ...')
- * @return int <0 if KO, array of time spent if OK
- */
- public function fetchTimeSpentOnTask($morewherefilter = '')
- {
- global $langs;
- $arrayres = array();
- $sql = "SELECT";
- $sql .= " s.rowid as socid,";
- $sql .= " s.nom as thirdparty_name,";
- $sql .= " s.email as thirdparty_email,";
- $sql .= " ptt.rowid,";
- $sql .= " ptt.ref_ext,";
- $sql .= " ptt.fk_element as fk_task,";
- $sql .= " ptt.element_date as task_date,";
- $sql .= " ptt.element_datehour as task_datehour,";
- $sql .= " ptt.element_date_withhour as task_date_withhour,";
- $sql .= " ptt.element_duration as task_duration,";
- $sql .= " ptt.fk_user,";
- $sql .= " ptt.note,";
- $sql .= " ptt.thm,";
- $sql .= " pt.rowid as task_id,";
- $sql .= " pt.ref as task_ref,";
- $sql .= " pt.label as task_label,";
- $sql .= " p.rowid as project_id,";
- $sql .= " p.ref as project_ref,";
- $sql .= " p.title as project_label,";
- $sql .= " p.public as public";
- $sql .= " FROM ".MAIN_DB_PREFIX."element_time as ptt, ".MAIN_DB_PREFIX."projet_task as pt, ".MAIN_DB_PREFIX."projet as p";
- $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
- $sql .= " WHERE ptt.fk_element = pt.rowid AND pt.fk_projet = p.rowid";
- $sql .= " AND ptt.elementtype = 'task'";
- $sql .= " AND pt.rowid = ".((int) $this->id);
- $sql .= " AND pt.entity IN (".getEntity('project').")";
- if ($morewherefilter) {
- $sql .= $morewherefilter;
- }
- dol_syslog(get_class($this)."::fetchAllTimeSpent", LOG_DEBUG);
- $resql = $this->db->query($sql);
- if ($resql) {
- $num = $this->db->num_rows($resql);
- $i = 0;
- while ($i < $num) {
- $obj = $this->db->fetch_object($resql);
- $newobj = new stdClass();
- $newobj->socid = $obj->socid;
- $newobj->thirdparty_name = $obj->thirdparty_name;
- $newobj->thirdparty_email = $obj->thirdparty_email;
- $newobj->fk_project = $obj->project_id;
- $newobj->project_ref = $obj->project_ref;
- $newobj->project_label = $obj->project_label;
- $newobj->public = $obj->project_public;
- $newobj->fk_task = $obj->task_id;
- $newobj->task_ref = $obj->task_ref;
- $newobj->task_label = $obj->task_label;
- $newobj->timespent_line_id = $obj->rowid;
- $newobj->timespent_line_ref_ext = $obj->ref_ext;
- $newobj->timespent_line_date = $this->db->jdate($obj->task_date);
- $newobj->timespent_line_datehour = $this->db->jdate($obj->task_datehour);
- $newobj->timespent_line_withhour = $obj->task_date_withhour;
- $newobj->timespent_line_duration = $obj->task_duration;
- $newobj->timespent_line_fk_user = $obj->fk_user;
- $newobj->timespent_line_thm = $obj->thm; // hourly rate
- $newobj->timespent_line_note = $obj->note;
- $arrayres[] = $newobj;
- $i++;
- }
- $this->db->free($resql);
- $this->lines = $arrayres;
- return 1;
- } else {
- dol_print_error($this->db);
- $this->error = "Error ".$this->db->lasterror();
- return -1;
- }
- }
- /**
- * Calculate total of time spent for task
- *
- * @param User|int $userobj Filter on user. null or 0=No filter
- * @param string $morewherefilter Add more filter into where SQL request (must start with ' AND ...')
- * @return array|int Array of info for task array('min_date', 'max_date', 'total_duration', 'total_amount', 'nblines', 'nblinesnull')
- */
- public function getSummaryOfTimeSpent($userobj = null, $morewherefilter = '')
- {
- if (is_object($userobj)) {
- $userid = $userobj->id;
- } else {
- $userid = $userobj; // old method
- }
- $id = $this->id;
- if (empty($id) && empty($userid)) {
- dol_syslog("getSummaryOfTimeSpent called on a not loaded task without user param defined", LOG_ERR);
- return -1;
- }
- $result = array();
- $sql = "SELECT";
- $sql .= " MIN(t.element_datehour) as min_date,";
- $sql .= " MAX(t.element_datehour) as max_date,";
- $sql .= " SUM(t.element_duration) as total_duration,";
- $sql .= " SUM(t.element_duration / 3600 * ".$this->db->ifsql("t.thm IS NULL", 0, "t.thm").") as total_amount,";
- $sql .= " COUNT(t.rowid) as nblines,";
- $sql .= " SUM(".$this->db->ifsql("t.thm IS NULL", 1, 0).") as nblinesnull";
- $sql .= " FROM ".MAIN_DB_PREFIX."element_time as t";
- $sql .= " WHERE t.elementtype='task'";
- if ($morewherefilter) {
- $sql .= $morewherefilter;
- }
- if ($id > 0) {
- $sql .= " AND t.fk_element = ".((int) $id);
- }
- if ($userid > 0) {
- $sql .= " AND t.fk_user = ".((int) $userid);
- }
- dol_syslog(get_class($this)."::getSummaryOfTimeSpent", LOG_DEBUG);
- $resql = $this->db->query($sql);
- if ($resql) {
- $obj = $this->db->fetch_object($resql);
- $result['min_date'] = $obj->min_date; // deprecated. use the ->timespent_xxx instead
- $result['max_date'] = $obj->max_date; // deprecated. use the ->timespent_xxx instead
- $result['total_duration'] = $obj->total_duration; // deprecated. use the ->timespent_xxx instead
- $this->timespent_min_date = $this->db->jdate($obj->min_date);
- $this->timespent_max_date = $this->db->jdate($obj->max_date);
- $this->timespent_total_duration = $obj->total_duration;
- $this->timespent_total_amount = $obj->total_amount;
- $this->timespent_nblinesnull = ($obj->nblinesnull ? $obj->nblinesnull : 0);
- $this->timespent_nblines = ($obj->nblines ? $obj->nblines : 0);
- $this->db->free($resql);
- } else {
- dol_print_error($this->db);
- }
- return $result;
- }
- /**
- * Calculate quantity and value of time consumed using the thm (hourly amount value of work for user entering time)
- *
- * @param User $fuser Filter on a dedicated user
- * @param string $dates Start date (ex 00:00:00)
- * @param string $datee End date (ex 23:59:59)
- * @return array Array of info for task array('amount','nbseconds','nblinesnull')
- */
- public function getSumOfAmount($fuser = '', $dates = '', $datee = '')
- {
- global $langs;
- if (empty($id)) {
- $id = $this->id;
- }
- $result = array();
- $sql = "SELECT";
- $sql .= " SUM(t.element_duration) as nbseconds,";
- $sql .= " SUM(t.element_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";
- $sql .= " FROM ".MAIN_DB_PREFIX."element_time as t";
- $sql .= " WHERE t.elementtype='task' AND t.fk_element = ".((int) $id);
- if (is_object($fuser) && $fuser->id > 0) {
- $sql .= " AND fk_user = ".((int) $fuser->id);
- }
- if ($dates > 0) {
- $datefieldname = "element_datehour";
- $sql .= " AND (".$datefieldname." >= '".$this->db->idate($dates)."' OR ".$datefieldname." IS NULL)";
- }
- if ($datee > 0) {
- $datefieldname = "element_datehour";
- $sql .= " AND (".$datefieldname." <= '".$this->db->idate($datee)."' OR ".$datefieldname." IS NULL)";
- }
- //print $sql;
- dol_syslog(get_class($this)."::getSumOfAmount", LOG_DEBUG);
- $resql = $this->db->query($sql);
- if ($resql) {
- $obj = $this->db->fetch_object($resql);
- $result['amount'] = $obj->amount;
- $result['nbseconds'] = $obj->nbseconds;
- $result['nblinesnull'] = $obj->nblinesnull;
- $this->db->free($resql);
- return $result;
- } else {
- dol_print_error($this->db);
- return $result;
- }
- }
- /**
- * Load properties of timespent of a task from the time spent ID.
- *
- * @param int $id Id in time spent table
- * @return int <0 if KO, >0 if OK
- */
- public function fetchTimeSpent($id)
- {
- global $langs;
- $timespent = new TimeSpent($this->db);
- $timespent->fetch($id);
- dol_syslog(get_class($this)."::fetchTimeSpent", LOG_DEBUG);
- if ($timespent->id > 0) {
- $this->timespent_id = $timespent->id;
- $this->id = $timespent->fk_element;
- $this->timespent_date = $timespent->element_date;
- $this->timespent_datehour = $timespent->element_datehour;
- $this->timespent_withhour = $timespent->element_date_withhour;
- $this->timespent_duration = $timespent->element_duration;
- $this->timespent_fk_user = $timespent->fk_user;
- $this->timespent_fk_product = $timespent->fk_product;
- $this->timespent_thm = $timespent->thm; // hourly rate
- $this->timespent_note = $timespent->note;
- return 1;
- }
- return 0;
- }
- /**
- * Load all records of time spent
- *
- * @param User $userobj User object
- * @param string $morewherefilter Add more filter into where SQL request (must start with ' AND ...')
- * @return array|int <0 if KO, array of time spent if OK
- */
- public function fetchAllTimeSpent(User $userobj, $morewherefilter = '')
- {
- $arrayres = array();
- $sql = "SELECT";
- $sql .= " s.rowid as socid,";
- $sql .= " s.nom as thirdparty_name,";
- $sql .= " s.email as thirdparty_email,";
- $sql .= " ptt.rowid,";
- $sql .= " ptt.fk_element as fk_task,";
- $sql .= " ptt.element_date as task_date,";
- $sql .= " ptt.element_datehour as task_datehour,";
- $sql .= " ptt.element_date_withhour as task_date_withhour,";
- $sql .= " ptt.element_duration as task_duration,";
- $sql .= " ptt.fk_user,";
- $sql .= " ptt.note,";
- $sql .= " ptt.thm,";
- $sql .= " pt.rowid as task_id,";
- $sql .= " pt.ref as task_ref,";
- $sql .= " pt.label as task_label,";
- $sql .= " p.rowid as project_id,";
- $sql .= " p.ref as project_ref,";
- $sql .= " p.title as project_label,";
- $sql .= " p.public as public";
- $sql .= " FROM ".MAIN_DB_PREFIX."element_time as ptt, ".MAIN_DB_PREFIX."projet_task as pt, ".MAIN_DB_PREFIX."projet as p";
- $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
- $sql .= " WHERE ptt.fk_element = pt.rowid AND pt.fk_projet = p.rowid";
- $sql .= " AND ptt.elementtype = 'task'";
- $sql .= " AND ptt.fk_user = ".((int) $userobj->id);
- $sql .= " AND pt.entity IN (".getEntity('project').")";
- if ($morewherefilter) {
- $sql .= $morewherefilter;
- }
- dol_syslog(get_class($this)."::fetchAllTimeSpent", LOG_DEBUG);
- $resql = $this->db->query($sql);
- if ($resql) {
- $num = $this->db->num_rows($resql);
- $i = 0;
- while ($i < $num) {
- $obj = $this->db->fetch_object($resql);
- $newobj = new stdClass();
- $newobj->socid = $obj->socid;
- $newobj->thirdparty_name = $obj->thirdparty_name;
- $newobj->thirdparty_email = $obj->thirdparty_email;
- $newobj->fk_project = $obj->project_id;
- $newobj->project_ref = $obj->project_ref;
- $newobj->project_label = $obj->project_label;
- $newobj->public = $obj->project_public;
- $newobj->fk_task = $obj->task_id;
- $newobj->task_ref = $obj->task_ref;
- $newobj->task_label = $obj->task_label;
- $newobj->timespent_id = $obj->rowid;
- $newobj->timespent_date = $this->db->jdate($obj->task_date);
- $newobj->timespent_datehour = $this->db->jdate($obj->task_datehour);
- $newobj->timespent_withhour = $obj->task_date_withhour;
- $newobj->timespent_duration = $obj->task_duration;
- $newobj->timespent_fk_user = $obj->fk_user;
- $newobj->timespent_thm = $obj->thm; // hourly rate
- $newobj->timespent_note = $obj->note;
- $arrayres[] = $newobj;
- $i++;
- }
- $this->db->free($resql);
- } else {
- dol_print_error($this->db);
- $this->error = "Error ".$this->db->lasterror();
- return -1;
- }
- return $arrayres;
- }
- /**
- * Update time spent
- *
- * @param User $user User id
- * @param int $notrigger 0=launch triggers after, 1=disable triggers
- * @return int <0 if KO, >0 if OK
- */
- public function updateTimeSpent($user, $notrigger = 0)
- {
- global $conf, $langs;
- $ret = 0;
- // Check parameters
- if ($this->timespent_date == '') {
- $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentities("Date"));
- return -1;
- }
- if (!($this->timespent_fk_user > 0)) {
- $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentities("User"));
- return -1;
- }
- // Clean parameters
- if (empty($this->timespent_datehour)) {
- $this->timespent_datehour = $this->timespent_date;
- }
- if (isset($this->timespent_note)) {
- $this->timespent_note = trim($this->timespent_note);
- }
- if (!empty($conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS)) {
- require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
- $restrictBefore = dol_time_plus_duree(dol_now(), - $conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS, 'm');
- if ($this->timespent_date < $restrictBefore) {
- $this->error = $langs->trans('TimeRecordingRestrictedToNMonthsBack', $conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS);
- $this->errors[] = $this->error;
- return -1;
- }
- }
- $this->db->begin();
- $timespent = new TimeSpent($this->db);
- $timespent->fetch($this->timespent_id);
- $timespent->element_date = $this->timespent_date;
- $timespent->element_datehour = $this->timespent_datehour;
- $timespent->element_date_withhour = $this->timespent_withhour;
- $timespent->element_duration = $this->timespent_duration;
- $timespent->fk_user = $this->timespent_fk_user;
- $timespent->fk_product = $this->timespent_fk_product;
- $timespent->note = $this->timespent_note;
- $timespent->invoice_id = $this->timespent_invoiceid;
- $timespent->invoice_line_id = $this->timespent_invoicelineid;
- dol_syslog(get_class($this)."::updateTimeSpent", LOG_DEBUG);
- if ($timespent->update($user) > 0) {
- if (!$notrigger) {
- // Call trigger
- $result = $this->call_trigger('TASK_TIMESPENT_MODIFY', $user);
- if ($result < 0) {
- $this->db->rollback();
- $ret = -1;
- } else {
- $ret = 1;
- }
- // End call triggers
- } else {
- $ret = 1;
- }
- } else {
- $this->error = $this->db->lasterror();
- $this->db->rollback();
- $ret = -1;
- }
- if ($ret == 1 && (($this->timespent_old_duration != $this->timespent_duration) || !empty($conf->global->TIMESPENT_ALWAYS_UPDATE_THM))) {
- if ($this->timespent_old_duration != $this->timespent_duration) {
- // Recalculate amount of time spent for task and update denormalized field
- $sql = "UPDATE " . MAIN_DB_PREFIX . "projet_task";
- $sql .= " SET duration_effective = (SELECT SUM(element_duration) FROM " . MAIN_DB_PREFIX . "element_time as ptt where ptt.elementtype = 'task' AND ptt.fk_element = " . ((int) $this->id) . ")";
- if (isset($this->progress)) {
- $sql .= ", progress = " . ((float) $this->progress); // Do not overwrite value if not provided
- }
- $sql .= " WHERE rowid = " . ((int) $this->id);
- dol_syslog(get_class($this) . "::updateTimeSpent", LOG_DEBUG);
- if (!$this->db->query($sql)) {
- $this->error = $this->db->lasterror();
- $this->db->rollback();
- $ret = -2;
- }
- }
- // Update hourly rate of this time spent entry, but only if it was not set initialy
- $res_update = 1;
- if (empty($timespent->thm) || !empty($conf->global->TIMESPENT_ALWAYS_UPDATE_THM)) {
- $resql_thm_user = $this->db->query("SELECT thm FROM " . MAIN_DB_PREFIX . "user WHERE rowid = " . ((int) $timespent->fk_user));
- if (!empty($resql_thm_user)) {
- $obj_thm_user = $this->db->fetch_object($resql_thm_user);
- $timespent->thm = $obj_thm_user->thm;
- }
- $res_update = $timespent->update($user);
- }
- dol_syslog(get_class($this)."::updateTimeSpent", LOG_DEBUG);
- if ($res_update <= 0) {
- $this->error = $this->db->lasterror();
- $ret = -2;
- }
- }
- if ($ret >= 0) {
- $this->db->commit();
- }
- return $ret;
- }
- /**
- * Delete time spent
- *
- * @param User $user User that delete
- * @param int $notrigger 0=launch triggers after, 1=disable triggers
- * @return int <0 if KO, >0 if OK
- */
- public function delTimeSpent($user, $notrigger = 0)
- {
- global $conf, $langs;
- $error = 0;
- if (!empty($conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS)) {
- require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
- $restrictBefore = dol_time_plus_duree(dol_now(), - $conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS, 'm');
- if ($this->timespent_date < $restrictBefore) {
- $this->error = $langs->trans('TimeRecordingRestrictedToNMonthsBack', $conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS);
- $this->errors[] = $this->error;
- return -1;
- }
- }
- $this->db->begin();
- if (!$notrigger) {
- // Call trigger
- $result = $this->call_trigger('TASK_TIMESPENT_DELETE', $user);
- if ($result < 0) {
- $error++;
- }
- // End call triggers
- }
- if (!$error) {
- $timespent = new TimeSpent($this->db);
- $timespent->fetch($this->timespent_id);
- $res_del = $timespent->delete($user);
- if ($res_del < 0) {
- $error++; $this->errors[] = "Error ".$this->db->lasterror();
- }
- }
- if (!$error) {
- $sql = "UPDATE ".MAIN_DB_PREFIX."projet_task";
- $sql .= " SET duration_effective = duration_effective - ".$this->db->escape($this->timespent_duration ? $this->timespent_duration : 0);
- $sql .= " WHERE rowid = ".((int) $this->id);
- dol_syslog(get_class($this)."::delTimeSpent", LOG_DEBUG);
- if ($this->db->query($sql)) {
- $result = 0;
- } else {
- $this->error = $this->db->lasterror();
- $result = -2;
- }
- }
- // Commit or rollback
- if ($error) {
- foreach ($this->errors as $errmsg) {
- dol_syslog(get_class($this)."::delTimeSpent ".$errmsg, LOG_ERR);
- $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
- }
- $this->db->rollback();
- return -1 * $error;
- } else {
- $this->db->commit();
- return 1;
- }
- }
- /** Load an object from its id and create a new one in database
- *
- * @param User $user User making the clone
- * @param int $fromid Id of object to clone
- * @param int $project_id Id of project to attach clone task
- * @param int $parent_task_id Id of task to attach clone task
- * @param bool $clone_change_dt recalculate date of task regarding new project start date
- * @param bool $clone_affectation clone affectation of project
- * @param bool $clone_time clone time of project
- * @param bool $clone_file clone file of project
- * @param bool $clone_note clone note of project
- * @param bool $clone_prog clone progress of project
- * @return int New id of clone
- */
- 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)
- {
- global $langs, $conf;
- $error = 0;
- //Use 00:00 of today if time is use on task.
- $now = dol_mktime(0, 0, 0, dol_print_date(dol_now(), '%m'), dol_print_date(dol_now(), '%d'), dol_print_date(dol_now(), '%Y'));
- $datec = $now;
- $clone_task = new Task($this->db);
- $origin_task = new Task($this->db);
- $clone_task->context['createfromclone'] = 'createfromclone';
- $this->db->begin();
- // Load source object
- $clone_task->fetch($fromid);
- $clone_task->fetch_optionals();
- //var_dump($clone_task->array_options);exit;
- $origin_task->fetch($fromid);
- $defaultref = '';
- $obj = empty($conf->global->PROJECT_TASK_ADDON) ? 'mod_task_simple' : $conf->global->PROJECT_TASK_ADDON;
- if (!empty($conf->global->PROJECT_TASK_ADDON) && is_readable(DOL_DOCUMENT_ROOT."/core/modules/project/task/" . getDolGlobalString('PROJECT_TASK_ADDON').".php")) {
- require_once DOL_DOCUMENT_ROOT."/core/modules/project/task/" . getDolGlobalString('PROJECT_TASK_ADDON').'.php';
- $modTask = new $obj;
- $defaultref = $modTask->getNextValue(0, $clone_task);
- }
- $ori_project_id = $clone_task->fk_project;
- $clone_task->id = 0;
- $clone_task->ref = $defaultref;
- $clone_task->fk_project = $project_id;
- $clone_task->fk_task_parent = $parent_task_id;
- $clone_task->date_c = $datec;
- $clone_task->planned_workload = $origin_task->planned_workload;
- $clone_task->rang = $origin_task->rang;
- //Manage Task Date
- if ($clone_change_dt) {
- $projectstatic = new Project($this->db);
- $projectstatic->fetch($ori_project_id);
- //Origin project strat date
- $orign_project_dt_start = $projectstatic->date_start;
- //Calcultate new task start date with difference between origin proj start date and origin task start date
- if (!empty($clone_task->date_start)) {
- $clone_task->date_start = $now + $clone_task->date_start - $orign_project_dt_start;
- }
- //Calcultate new task end date with difference between origin proj end date and origin task end date
- if (!empty($clone_task->date_end)) {
- $clone_task->date_end = $now + $clone_task->date_end - $orign_project_dt_start;
- }
- }
- if (!$clone_prog) {
- $clone_task->progress = 0;
- }
- // Create clone
- $result = $clone_task->create($user);
- // Other options
- if ($result < 0) {
- $this->error = $clone_task->error;
- $error++;
- }
- // End
- if (!$error) {
- $clone_task_id = $clone_task->id;
- $clone_task_ref = $clone_task->ref;
- //Note Update
- if (!$clone_note) {
- $clone_task->note_private = '';
- $clone_task->note_public = '';
- } else {
- $this->db->begin();
- $res = $clone_task->update_note(dol_html_entity_decode($clone_task->note_public, ENT_QUOTES | ENT_HTML5), '_public');
- if ($res < 0) {
- $this->error .= $clone_task->error;
- $error++;
- $this->db->rollback();
- } else {
- $this->db->commit();
- }
- $this->db->begin();
- $res = $clone_task->update_note(dol_html_entity_decode($clone_task->note_private, ENT_QUOTES | ENT_HTML5), '_private');
- if ($res < 0) {
- $this->error .= $clone_task->error;
- $error++;
- $this->db->rollback();
- } else {
- $this->db->commit();
- }
- }
- //Duplicate file
- if ($clone_file) {
- require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
- //retrieve project origin ref to know folder to copy
- $projectstatic = new Project($this->db);
- $projectstatic->fetch($ori_project_id);
- $ori_project_ref = $projectstatic->ref;
- if ($ori_project_id != $project_id) {
- $projectstatic->fetch($project_id);
- $clone_project_ref = $projectstatic->ref;
- } else {
- $clone_project_ref = $ori_project_ref;
- }
- $clone_task_dir = $conf->project->dir_output."/".dol_sanitizeFileName($clone_project_ref)."/".dol_sanitizeFileName($clone_task_ref);
- $ori_task_dir = $conf->project->dir_output."/".dol_sanitizeFileName($ori_project_ref)."/".dol_sanitizeFileName($fromid);
- $filearray = dol_dir_list($ori_task_dir, "files", 0, '', '(\.meta|_preview.*\.png)$', '', SORT_ASC, 1);
- foreach ($filearray as $key => $file) {
- if (!file_exists($clone_task_dir)) {
- if (dol_mkdir($clone_task_dir) < 0) {
- $this->error .= $langs->trans('ErrorInternalErrorDetected').':dol_mkdir';
- $error++;
- }
- }
- $rescopy = dol_copy($ori_task_dir.'/'.$file['name'], $clone_task_dir.'/'.$file['name'], 0, 1);
- if (is_numeric($rescopy) && $rescopy < 0) {
- $this->error .= $langs->trans("ErrorFailToCopyFile", $ori_task_dir.'/'.$file['name'], $clone_task_dir.'/'.$file['name']);
- $error++;
- }
- }
- }
- // clone affectation
- if ($clone_affectation) {
- $origin_task = new Task($this->db);
- $origin_task->fetch($fromid);
- foreach (array('internal', 'external') as $source) {
- $tab = $origin_task->liste_contact(-1, $source);
- $num = count($tab);
- $i = 0;
- while ($i < $num) {
- $clone_task->add_contact($tab[$i]['id'], $tab[$i]['code'], $tab[$i]['source']);
- if ($clone_task->error == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
- $langs->load("errors");
- $this->error .= $langs->trans("ErrorThisContactIsAlreadyDefinedAsThisType");
- $error++;
- } else {
- if ($clone_task->error != '') {
- $this->error .= $clone_task->error;
- $error++;
- }
- }
- $i++;
- }
- }
- }
- if ($clone_time) {
- //TODO clone time of affectation
- }
- }
- unset($clone_task->context['createfromclone']);
- if (!$error) {
- $this->db->commit();
- return $clone_task_id;
- } else {
- $this->db->rollback();
- dol_syslog(get_class($this)."::createFromClone nbError: ".$error." error : ".$this->error, LOG_ERR);
- return -1;
- }
- }
- /**
- * Return status label of object
- *
- * @param integer $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto
- * @return string Label
- */
- public function getLibStatut($mode = 0)
- {
- return $this->LibStatut($this->fk_statut, $mode);
- }
- // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
- /**
- * Return status label for an object
- *
- * @param int $status Id status
- * @param integer $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto
- * @return string Label
- */
- public function LibStatut($status, $mode = 0)
- {
- // phpcs:enable
- global $langs;
- // list of Statut of the task
- $this->statuts[0] = 'Draft';
- $this->statuts[1] = 'ToDo';
- $this->statuts[2] = 'Running';
- $this->statuts[3] = 'Finish';
- $this->statuts[4] = 'Transfered';
- $this->statuts_short[0] = 'Draft';
- $this->statuts_short[1] = 'ToDo';
- $this->statuts_short[2] = 'Running';
- $this->statuts_short[3] = 'Completed';
- $this->statuts_short[4] = 'Transfered';
- if ($mode == 0) {
- return $langs->trans($this->statuts[$status]);
- } elseif ($mode == 1) {
- return $langs->trans($this->statuts_short[$status]);
- } elseif ($mode == 2) {
- if ($status == 0) {
- return img_picto($langs->trans($this->statuts_short[$status]), 'statut0').' '.$langs->trans($this->statuts_short[$status]);
- } elseif ($status == 1) {
- return img_picto($langs->trans($this->statuts_short[$status]), 'statut1').' '.$langs->trans($this->statuts_short[$status]);
- } elseif ($status == 2) {
- return img_picto($langs->trans($this->statuts_short[$status]), 'statut3').' '.$langs->trans($this->statuts_short[$status]);
- } elseif ($status == 3) {
- return img_picto($langs->trans($this->statuts_short[$status]), 'statut6').' '.$langs->trans($this->statuts_short[$status]);
- } elseif ($status == 4) {
- return img_picto($langs->trans($this->statuts_short[$status]), 'statut6').' '.$langs->trans($this->statuts_short[$status]);
- } elseif ($status == 5) {
- return img_picto($langs->trans($this->statuts_short[$status]), 'statut5').' '.$langs->trans($this->statuts_short[$status]);
- }
- } elseif ($mode == 3) {
- if ($status == 0) {
- return img_picto($langs->trans($this->statuts_short[$status]), 'statut0');
- } elseif ($status == 1) {
- return img_picto($langs->trans($this->statuts_short[$status]), 'statut1');
- } elseif ($status == 2) {
- return img_picto($langs->trans($this->statuts_short[$status]), 'statut3');
- } elseif ($status == 3) {
- return img_picto($langs->trans($this->statuts_short[$status]), 'statut6');
- } elseif ($status == 4) {
- return img_picto($langs->trans($this->statuts_short[$status]), 'statut6');
- } elseif ($status == 5) {
- return img_picto($langs->trans($this->statuts_short[$status]), 'statut5');
- }
- } elseif ($mode == 4) {
- if ($status == 0) {
- return img_picto($langs->trans($this->statuts_short[$status]), 'statut0').' '.$langs->trans($this->statuts[$status]);
- } elseif ($status == 1) {
- return img_picto($langs->trans($this->statuts_short[$status]), 'statut1').' '.$langs->trans($this->statuts[$status]);
- } elseif ($status == 2) {
- return img_picto($langs->trans($this->statuts_short[$status]), 'statut3').' '.$langs->trans($this->statuts[$status]);
- } elseif ($status == 3) {
- return img_picto($langs->trans($this->statuts_short[$status]), 'statut6').' '.$langs->trans($this->statuts[$status]);
- } elseif ($status == 4) {
- return img_picto($langs->trans($this->statuts_short[$status]), 'statut6').' '.$langs->trans($this->statuts[$status]);
- } elseif ($status == 5) {
- return img_picto($langs->trans($this->statuts_short[$status]), 'statut5').' '.$langs->trans($this->statuts[$status]);
- }
- } elseif ($mode == 5) {
- /*if ($status==0) return $langs->trans($this->statuts_short[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut0');
- elseif ($status==1) return $langs->trans($this->statuts_short[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut1');
- elseif ($status==2) return $langs->trans($this->statuts_short[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut3');
- elseif ($status==3) return $langs->trans($this->statuts_short[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut6');
- elseif ($status==4) return $langs->trans($this->statuts_short[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut6');
- elseif ($status==5) return $langs->trans($this->statuts_short[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut5');
- */
- //else return $this->progress.' %';
- return ' ';
- } elseif ($mode == 6) {
- /*if ($status==0) return $langs->trans($this->statuts[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut0');
- elseif ($status==1) return $langs->trans($this->statuts[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut1');
- elseif ($status==2) return $langs->trans($this->statuts[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut3');
- elseif ($status==3) return $langs->trans($this->statuts[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut6');
- elseif ($status==4) return $langs->trans($this->statuts[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut6');
- elseif ($status==5) return $langs->trans($this->statuts[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut5');
- */
- //else return $this->progress.' %';
- return ' ';
- }
- return "";
- }
- /**
- * Create an intervention document on disk using template defined into PROJECT_TASK_ADDON_PDF
- *
- * @param string $modele force le modele a utiliser ('' par defaut)
- * @param Translate $outputlangs objet lang a utiliser pour traduction
- * @param int $hidedetails Hide details of lines
- * @param int $hidedesc Hide description
- * @param int $hideref Hide ref
- * @return int 0 if KO, 1 if OK
- */
- public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0)
- {
- global $conf;
- $outputlangs->load("projects");
- if (!dol_strlen($modele)) {
- $modele = 'nodefault';
- if (!empty($this->model_pdf)) {
- $modele = $this->model_pdf;
- } elseif (!empty($conf->global->PROJECT_TASK_ADDON_PDF)) {
- $modele = $conf->global->PROJECT_TASK_ADDON_PDF;
- }
- }
- $modelpath = "core/modules/project/task/doc/";
- return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref);
- }
- // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
- /**
- * Load indicators for dashboard (this->nbtodo and this->nbtodolate)
- *
- * @param User $user Objet user
- * @return WorkboardResponse|int <0 if KO, WorkboardResponse if OK
- */
- public function load_board($user)
- {
- // phpcs:enable
- global $conf, $langs;
- // For external user, no check is done on company because readability is managed by public status of project and assignement.
- //$socid = $user->socid;
- $socid = 0;
- $projectstatic = new Project($this->db);
- $projectsListId = $projectstatic->getProjectsAuthorizedForUser($user, 0, 1, $socid);
- // List of tasks (does not care about permissions. Filtering will be done later)
- $sql = "SELECT p.rowid as projectid, p.fk_statut as projectstatus,";
- $sql .= " t.rowid as taskid, t.progress as progress, t.fk_statut as status,";
- $sql .= " t.dateo as date_start, t.datee as datee";
- $sql .= " FROM ".MAIN_DB_PREFIX."projet as p";
- //$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s on p.fk_soc = s.rowid";
- //if (! $user->rights->societe->client->voir && ! $socid) $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON sc.fk_soc = s.rowid";
- $sql .= ", ".MAIN_DB_PREFIX."projet_task as t";
- $sql .= " WHERE p.entity IN (".getEntity('project', 0).')';
- $sql .= " AND p.fk_statut = 1";
- $sql .= " AND t.fk_projet = p.rowid";
- $sql .= " AND (t.progress IS NULL OR t.progress < 100)"; // tasks to do
- if (!$user->hasRight('projet', 'all', 'lire')) {
- $sql .= " AND p.rowid IN (".$this->db->sanitize($projectsListId).")";
- }
- // No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser
- //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).")";
- // No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser
- // 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))";
- //print $sql;
- $resql = $this->db->query($sql);
- if ($resql) {
- $task_static = new Task($this->db);
- $response = new WorkboardResponse();
- $response->warning_delay = $conf->project->task->warning_delay / 60 / 60 / 24;
- $response->label = $langs->trans("OpenedTasks");
- if ($user->hasRight("projet", "all", "lire")) {
- $response->url = DOL_URL_ROOT.'/projet/tasks/list.php?mainmenu=project';
- } else {
- $response->url = DOL_URL_ROOT.'/projet/tasks/list.php?mode=mine&mainmenu=project';
- }
- $response->img = img_object('', "task");
- // This assignment in condition is not a bug. It allows walking the results.
- while ($obj = $this->db->fetch_object($resql)) {
- $response->nbtodo++;
- $task_static->projectstatus = $obj->projectstatus;
- $task_static->progress = $obj->progress;
- $task_static->fk_statut = $obj->status;
- $task_static->date_end = $this->db->jdate($obj->datee);
- if ($task_static->hasDelay()) {
- $response->nbtodolate++;
- }
- }
- return $response;
- } else {
- $this->error = $this->db->error();
- return -1;
- }
- }
- // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
- /**
- * Charge indicateurs this->nb de tableau de bord
- *
- * @return int <0 if ko, >0 if ok
- */
- public function load_state_board()
- {
- // phpcs:enable
- global $user;
- $mine = 0; $socid = $user->socid;
- $projectstatic = new Project($this->db);
- $projectsListId = $projectstatic->getProjectsAuthorizedForUser($user, $mine, 1, $socid);
- // List of tasks (does not care about permissions. Filtering will be done later)
- $sql = "SELECT count(p.rowid) as nb";
- $sql .= " FROM ".MAIN_DB_PREFIX."projet as p";
- $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s on p.fk_soc = s.rowid";
- if (!$user->hasRight('societe', 'client', 'voir') && !$socid) {
- $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON sc.fk_soc = s.rowid";
- }
- $sql .= ", ".MAIN_DB_PREFIX."projet_task as t";
- $sql .= " WHERE p.entity IN (".getEntity('project', 0).')';
- $sql .= " AND t.fk_projet = p.rowid"; // tasks to do
- if ($mine || !$user->hasRight('projet', 'all', 'lire')) {
- $sql .= " AND p.rowid IN (".$this->db->sanitize($projectsListId).")";
- }
- // No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser
- //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).")";
- if ($socid) {
- $sql .= " AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = ".((int) $socid).")";
- }
- if (!$user->hasRight('societe', 'client', 'voir') && !$socid) {
- $sql .= " AND ((s.rowid = sc.fk_soc AND sc.fk_user = ".((int) $user->id).") OR (s.rowid IS NULL))";
- }
- $resql = $this->db->query($sql);
- if ($resql) {
- // This assignment in condition is not a bug. It allows walking the results.
- while ($obj = $this->db->fetch_object($resql)) {
- $this->nb["tasks"] = $obj->nb;
- }
- $this->db->free($resql);
- return 1;
- } else {
- dol_print_error($this->db);
- $this->error = $this->db->error();
- return -1;
- }
- }
- /**
- * Is the task delayed?
- *
- * @return bool
- */
- public function hasDelay()
- {
- global $conf;
- if (!($this->progress >= 0 && $this->progress < 100)) {
- return false;
- }
- $now = dol_now();
- $datetouse = ($this->date_end > 0) ? $this->date_end : ((isset($this->datee) && $this->datee > 0) ? $this->datee : 0);
- return ($datetouse > 0 && ($datetouse < ($now - $conf->project->task->warning_delay)));
- }
- /**
- * Return clicable link of object (with eventually picto)
- *
- * @param string $option Where point the link (0=> main card, 1,2 => shipment, 'nolink'=>No link)
- * @param array $arraydata Array of data
- * @return string HTML Code for Kanban thumb.
- */
- public function getKanbanView($option = '', $arraydata = null)
- {
- $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
- $return = '<div class="box-flex-item box-flex-grow-zero">';
- $return .= '<div class="info-box info-box-sm info-box-kanban">';
- $return .= '<span class="info-box-icon bg-infobox-action">';
- $return .= img_picto('', $this->picto);
- //$return .= '<i class="fa fa-dol-action"></i>'; // Can be image
- $return .= '</span>';
- $return .= '<div class="info-box-content">';
- $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl(1) : $this->ref).'</span>';
- $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
- if (!empty($arraydata['projectlink'])) {
- //$tmpproject = $arraydata['project'];
- //$return .= '<br><span class="info-box-status ">'.$tmpproject->getNomProject().'</span>';
- $return .= '<br><span class="info-box-status ">'.$arraydata['projectlink'].'</span>';
- }
- if (property_exists($this, 'budget_amount')) {
- //$return .= '<br><span class="info-box-label amount">'.$langs->trans("Budget").' : '.price($this->budget_amount, 0, $langs, 1, 0, 0, $conf->currency).'</span>';
- }
- if (property_exists($this, 'duration_effective')) {
- $return .= '<br><br><div class="info-box-label progressinkanban">'.getTaskProgressView($this, false, true).'</div>';
- }
- $return .= '</div>';
- $return .= '</div>';
- $return .= '</div>';
- return $return;
- }
- }
|