From 18ec65b4aff5b58923f44449295c6df5fd0c4d69 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 19 May 2020 22:06:17 +0200 Subject: [PATCH 1/4] Fix: action on an ID must not include test on entity. --- htdocs/projet/class/project.class.php | 3555 ++++++++++++------------- 1 file changed, 1777 insertions(+), 1778 deletions(-) diff --git a/htdocs/projet/class/project.class.php b/htdocs/projet/class/project.class.php index 5802ad20e56..78f5bac2fe7 100644 --- a/htdocs/projet/class/project.class.php +++ b/htdocs/projet/class/project.class.php @@ -34,27 +34,27 @@ require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php'; class Project extends CommonObject { - /** + /** * @var string ID to identify managed object */ public $element = 'project'; - /** + /** * @var string Name of table without prefix where object is stored */ public $table_element = 'projet'; - /** + /** * @var int Name of subtable line */ public $table_element_line = 'projet_task'; - /** + /** * @var int Name of field date */ public $table_element_date; - /** + /** * @var int Field with ID of parent key if this field has a parent */ public $fk_element = 'fk_projet'; @@ -63,19 +63,19 @@ class Project extends CommonObject * 0=No test on entity, 1=Test with field entity, 2=Test with link by societe * @var int */ - public $ismultientitymanaged = 1; + public $ismultientitymanaged = 1; - /** - * @var string String with name of icon for myobject. Must be the part after the 'object_' into object_myobject.png - */ - public $picto = 'projectpub'; + /** + * @var string String with name of icon for myobject. Must be the part after the 'object_' into object_myobject.png + */ + public $picto = 'projectpub'; - /** - * {@inheritdoc} - */ - protected $table_ref_field = 'ref'; + /** + * {@inheritdoc} + */ + protected $table_ref_field = 'ref'; - /** + /** * @var string description */ public $description; @@ -83,41 +83,41 @@ class Project extends CommonObject /** * @var string */ - public $title; + public $title; - public $date_start; - public $date_end; - public $date_close; + public $date_start; + public $date_end; + public $date_close; - public $socid; // To store id of thirdparty - public $thirdparty_name; // To store name of thirdparty (defined only in some cases) + public $socid; // To store id of thirdparty + public $thirdparty_name; // To store name of thirdparty (defined only in some cases) - public $user_author_id; //!< Id of project creator. Not defined if shared project. + public $user_author_id; //!< Id of project creator. Not defined if shared project. - /** - * @var int user close id - */ - public $fk_user_close; + /** + * @var int user close id + */ + public $fk_user_close; - /** - * @var int user close id - */ + /** + * @var int user close id + */ public $user_close_id; - public $public; //!< Tell if this is a public or private project - public $budget_amount; - public $usage_bill_time; // Is the time spent on project must be invoiced or not + public $public; //!< Tell if this is a public or private project + public $budget_amount; + public $usage_bill_time; // Is the time spent on project must be invoiced or not - public $statuts_short; - public $statuts_long; + public $statuts_short; + public $statuts_long; - public $statut; // 0=draft, 1=opened, 2=closed - public $opp_status; // opportunity status, into table llx_c_lead_status + public $statut; // 0=draft, 1=opened, 2=closed + public $opp_status; // opportunity status, into table llx_c_lead_status public $opp_percent; // opportunity probability - public $oldcopy; + public $oldcopy; - public $weekWorkLoad; // Used to store workload details of a projet - public $weekWorkLoadPerTask; // Used to store workload details of tasks of a projet + public $weekWorkLoad; // Used to store workload details of a projet + public $weekWorkLoadPerTask; // Used to store workload details of tasks of a projet /** * @var int Creation date @@ -196,406 +196,406 @@ class Project extends CommonObject ); - /** - * Constructor - * - * @param DoliDB $db Database handler - */ - public function __construct($db) - { - $this->db = $db; + /** + * Constructor + * + * @param DoliDB $db Database handler + */ + public function __construct($db) + { + $this->db = $db; - $this->statuts_short = array(0 => 'Draft', 1 => 'Opened', 2 => 'Closed'); - $this->statuts_long = array(0 => 'Draft', 1 => 'Opened', 2 => 'Closed'); - } + $this->statuts_short = array(0 => 'Draft', 1 => 'Opened', 2 => 'Closed'); + $this->statuts_long = array(0 => 'Draft', 1 => 'Opened', 2 => 'Closed'); + } - /** - * Create a project into database - * - * @param User $user User making creation - * @param int $notrigger Disable triggers - * @return int <0 if KO, id of created project if OK - */ - public function create($user, $notrigger = 0) - { - global $conf, $langs; + /** + * Create a project into database + * + * @param User $user User making creation + * @param int $notrigger Disable triggers + * @return int <0 if KO, id of created project if OK + */ + public function create($user, $notrigger = 0) + { + global $conf, $langs; - $error = 0; - $ret = 0; + $error = 0; + $ret = 0; - $now = dol_now(); + $now = dol_now(); - // Clean parameters - $this->note_private = dol_substr($this->note_private, 0, 65535); - $this->note_public = dol_substr($this->note_public, 0, 65535); + // Clean parameters + $this->note_private = dol_substr($this->note_private, 0, 65535); + $this->note_public = dol_substr($this->note_public, 0, 65535); - // Check parameters - if (!trim($this->ref)) - { - $this->error = 'ErrorFieldsRequired'; - dol_syslog(get_class($this)."::create error -1 ref null", LOG_ERR); - return -1; - } - if (!empty($conf->global->PROJECT_THIRDPARTY_REQUIRED) && !($this->socid > 0)) - { - $this->error = 'ErrorFieldsRequired'; - dol_syslog(get_class($this)."::create error -1 thirdparty not defined and option PROJECT_THIRDPARTY_REQUIRED is set", LOG_ERR); - return -1; - } + // Check parameters + if (!trim($this->ref)) + { + $this->error = 'ErrorFieldsRequired'; + dol_syslog(get_class($this)."::create error -1 ref null", LOG_ERR); + return -1; + } + if (!empty($conf->global->PROJECT_THIRDPARTY_REQUIRED) && !($this->socid > 0)) + { + $this->error = 'ErrorFieldsRequired'; + dol_syslog(get_class($this)."::create error -1 thirdparty not defined and option PROJECT_THIRDPARTY_REQUIRED is set", LOG_ERR); + return -1; + } - // Create project - $this->db->begin(); + // Create project + $this->db->begin(); - $sql = "INSERT INTO ".MAIN_DB_PREFIX."projet ("; - $sql .= "ref"; - $sql .= ", title"; - $sql .= ", description"; - $sql .= ", fk_soc"; - $sql .= ", fk_user_creat"; - $sql .= ", fk_statut"; - $sql .= ", fk_opp_status"; - $sql .= ", opp_percent"; - $sql .= ", public"; - $sql .= ", datec"; - $sql .= ", dateo"; - $sql .= ", datee"; - $sql .= ", opp_amount"; - $sql .= ", budget_amount"; - $sql .= ", usage_opportunity"; - $sql .= ", usage_task"; - $sql .= ", usage_bill_time"; - $sql .= ", usage_organize_event"; - $sql .= ", note_private"; - $sql .= ", note_public"; - $sql .= ", entity"; - $sql .= ") VALUES ("; - $sql .= "'".$this->db->escape($this->ref)."'"; - $sql .= ", '".$this->db->escape($this->title)."'"; - $sql .= ", '".$this->db->escape($this->description)."'"; - $sql .= ", ".($this->socid > 0 ? $this->socid : "null"); - $sql .= ", ".$user->id; - $sql .= ", ".(is_numeric($this->statut) ? $this->statut : '0'); - $sql .= ", ".((is_numeric($this->opp_status) && $this->opp_status > 0) ? $this->opp_status : 'NULL'); - $sql .= ", ".(is_numeric($this->opp_percent) ? $this->opp_percent : 'NULL'); - $sql .= ", ".($this->public ? 1 : 0); - $sql .= ", '".$this->db->idate($now)."'"; - $sql .= ", ".($this->date_start != '' ? "'".$this->db->idate($this->date_start)."'" : 'null'); - $sql .= ", ".($this->date_end != '' ? "'".$this->db->idate($this->date_end)."'" : 'null'); - $sql .= ", ".(strcmp($this->opp_amount, '') ? price2num($this->opp_amount) : 'null'); - $sql .= ", ".(strcmp($this->budget_amount, '') ? price2num($this->budget_amount) : 'null'); - $sql .= ", ".($this->usage_opportunity ? 1 : 0); - $sql .= ", ".($this->usage_task ? 1 : 0); - $sql .= ", ".($this->usage_bill_time ? 1 : 0); - $sql .= ", ".($this->usage_organize_event ? 1 : 0); - $sql .= ", ".($this->note_private ? "'".$this->db->escape($this->note_private)."'" : 'null'); - $sql .= ", ".($this->note_public ? "'".$this->db->escape($this->note_public)."'" : 'null'); - $sql .= ", ".$conf->entity; - $sql .= ")"; + $sql = "INSERT INTO ".MAIN_DB_PREFIX."projet ("; + $sql .= "ref"; + $sql .= ", title"; + $sql .= ", description"; + $sql .= ", fk_soc"; + $sql .= ", fk_user_creat"; + $sql .= ", fk_statut"; + $sql .= ", fk_opp_status"; + $sql .= ", opp_percent"; + $sql .= ", public"; + $sql .= ", datec"; + $sql .= ", dateo"; + $sql .= ", datee"; + $sql .= ", opp_amount"; + $sql .= ", budget_amount"; + $sql .= ", usage_opportunity"; + $sql .= ", usage_task"; + $sql .= ", usage_bill_time"; + $sql .= ", usage_organize_event"; + $sql .= ", note_private"; + $sql .= ", note_public"; + $sql .= ", entity"; + $sql .= ") VALUES ("; + $sql .= "'".$this->db->escape($this->ref)."'"; + $sql .= ", '".$this->db->escape($this->title)."'"; + $sql .= ", '".$this->db->escape($this->description)."'"; + $sql .= ", ".($this->socid > 0 ? $this->socid : "null"); + $sql .= ", ".$user->id; + $sql .= ", ".(is_numeric($this->statut) ? $this->statut : '0'); + $sql .= ", ".((is_numeric($this->opp_status) && $this->opp_status > 0) ? $this->opp_status : 'NULL'); + $sql .= ", ".(is_numeric($this->opp_percent) ? $this->opp_percent : 'NULL'); + $sql .= ", ".($this->public ? 1 : 0); + $sql .= ", '".$this->db->idate($now)."'"; + $sql .= ", ".($this->date_start != '' ? "'".$this->db->idate($this->date_start)."'" : 'null'); + $sql .= ", ".($this->date_end != '' ? "'".$this->db->idate($this->date_end)."'" : 'null'); + $sql .= ", ".(strcmp($this->opp_amount, '') ? price2num($this->opp_amount) : 'null'); + $sql .= ", ".(strcmp($this->budget_amount, '') ? price2num($this->budget_amount) : 'null'); + $sql .= ", ".($this->usage_opportunity ? 1 : 0); + $sql .= ", ".($this->usage_task ? 1 : 0); + $sql .= ", ".($this->usage_bill_time ? 1 : 0); + $sql .= ", ".($this->usage_organize_event ? 1 : 0); + $sql .= ", ".($this->note_private ? "'".$this->db->escape($this->note_private)."'" : 'null'); + $sql .= ", ".($this->note_public ? "'".$this->db->escape($this->note_public)."'" : 'null'); + $sql .= ", ".$conf->entity; + $sql .= ")"; - dol_syslog(get_class($this)."::create", LOG_DEBUG); - $resql = $this->db->query($sql); - if ($resql) - { - $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."projet"); - $ret = $this->id; + dol_syslog(get_class($this)."::create", LOG_DEBUG); + $resql = $this->db->query($sql); + if ($resql) + { + $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."projet"); + $ret = $this->id; - if (!$notrigger) - { - // Call trigger - $result = $this->call_trigger('PROJECT_CREATE', $user); - if ($result < 0) { $error++; } - // End call triggers - } - } - else - { - $this->error = $this->db->lasterror(); - $this->errno = $this->db->lasterrno(); - $error++; - } + if (!$notrigger) + { + // Call trigger + $result = $this->call_trigger('PROJECT_CREATE', $user); + if ($result < 0) { $error++; } + // End call triggers + } + } + else + { + $this->error = $this->db->lasterror(); + $this->errno = $this->db->lasterrno(); + $error++; + } - // Update extrafield - if (!$error) { - if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used - { - $result = $this->insertExtraFields(); - if ($result < 0) - { - $error++; - } - } - } + // Update extrafield + if (!$error) { + if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used + { + $result = $this->insertExtraFields(); + if ($result < 0) + { + $error++; + } + } + } - if (!$error && !empty($conf->global->MAIN_DISABLEDRAFTSTATUS)) - { - $res = $this->setValid($user); - if ($res < 0) $error++; - } + if (!$error && !empty($conf->global->MAIN_DISABLEDRAFTSTATUS)) + { + $res = $this->setValid($user); + if ($res < 0) $error++; + } - if (!$error) - { - $this->db->commit(); - return $ret; - } - else - { - $this->db->rollback(); - return -1; - } - } + if (!$error) + { + $this->db->commit(); + return $ret; + } + else + { + $this->db->rollback(); + return -1; + } + } - /** - * Update a project - * - * @param User $user User object of making update - * @param int $notrigger 1=Disable all triggers - * @return int <=0 if KO, >0 if OK - */ - public function update($user, $notrigger = 0) - { - global $langs, $conf; + /** + * Update a project + * + * @param User $user User object of making update + * @param int $notrigger 1=Disable all triggers + * @return int <=0 if KO, >0 if OK + */ + public function update($user, $notrigger = 0) + { + global $langs, $conf; - $error = 0; + $error = 0; - // Clean parameters - $this->title = trim($this->title); - $this->description = trim($this->description); + // Clean parameters + $this->title = trim($this->title); + $this->description = trim($this->description); if ($this->opp_amount < 0) $this->opp_amount = ''; if ($this->opp_percent < 0) $this->opp_percent = ''; - if ($this->date_end && $this->date_end < $this->date_start) - { - $this->error = $langs->trans("ErrorDateEndLowerThanDateStart"); - $this->errors[] = $this->error; - $this->db->rollback(); - dol_syslog(get_class($this)."::update error -3 ".$this->error, LOG_ERR); - return -3; - } + if ($this->date_end && $this->date_end < $this->date_start) + { + $this->error = $langs->trans("ErrorDateEndLowerThanDateStart"); + $this->errors[] = $this->error; + $this->db->rollback(); + dol_syslog(get_class($this)."::update error -3 ".$this->error, LOG_ERR); + return -3; + } - if (dol_strlen(trim($this->ref)) > 0) - { - $this->db->begin(); + if (dol_strlen(trim($this->ref)) > 0) + { + $this->db->begin(); - $sql = "UPDATE ".MAIN_DB_PREFIX."projet SET"; - $sql .= " ref='".$this->db->escape($this->ref)."'"; - $sql .= ", title = '".$this->db->escape($this->title)."'"; - $sql .= ", description = '".$this->db->escape($this->description)."'"; - $sql .= ", fk_soc = ".($this->socid > 0 ? $this->socid : "null"); - $sql .= ", fk_statut = ".$this->statut; - $sql .= ", fk_opp_status = ".((is_numeric($this->opp_status) && $this->opp_status > 0) ? $this->opp_status : 'null'); + $sql = "UPDATE ".MAIN_DB_PREFIX."projet SET"; + $sql .= " ref='".$this->db->escape($this->ref)."'"; + $sql .= ", title = '".$this->db->escape($this->title)."'"; + $sql .= ", description = '".$this->db->escape($this->description)."'"; + $sql .= ", fk_soc = ".($this->socid > 0 ? $this->socid : "null"); + $sql .= ", fk_statut = ".$this->statut; + $sql .= ", fk_opp_status = ".((is_numeric($this->opp_status) && $this->opp_status > 0) ? $this->opp_status : 'null'); $sql .= ", opp_percent = ".((is_numeric($this->opp_percent) && $this->opp_percent != '') ? $this->opp_percent : 'null'); - $sql .= ", public = ".($this->public ? 1 : 0); - $sql .= ", datec=".($this->date_c != '' ? "'".$this->db->idate($this->date_c)."'" : '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 .= ", date_close=".($this->date_close != '' ? "'".$this->db->idate($this->date_close)."'" : 'null'); - $sql .= ", fk_user_close=".($this->fk_user_close > 0 ? $this->fk_user_close : "null"); - $sql .= ", opp_amount = ".(strcmp($this->opp_amount, '') ? price2num($this->opp_amount) : "null"); - $sql .= ", budget_amount = ".(strcmp($this->budget_amount, '') ? price2num($this->budget_amount) : "null"); - $sql .= ", fk_user_modif = ".$user->id; - $sql .= ", usage_opportunity = ".($this->usage_opportunity ? 1 : 0); - $sql .= ", usage_task = ".($this->usage_task ? 1 : 0); - $sql .= ", usage_bill_time = ".($this->usage_bill_time ? 1 : 0); - $sql .= ", usage_organize_event = ".($this->usage_organize_event ? 1 : 0); - $sql .= " WHERE rowid = ".$this->id; + $sql .= ", public = ".($this->public ? 1 : 0); + $sql .= ", datec=".($this->date_c != '' ? "'".$this->db->idate($this->date_c)."'" : '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 .= ", date_close=".($this->date_close != '' ? "'".$this->db->idate($this->date_close)."'" : 'null'); + $sql .= ", fk_user_close=".($this->fk_user_close > 0 ? $this->fk_user_close : "null"); + $sql .= ", opp_amount = ".(strcmp($this->opp_amount, '') ? price2num($this->opp_amount) : "null"); + $sql .= ", budget_amount = ".(strcmp($this->budget_amount, '') ? price2num($this->budget_amount) : "null"); + $sql .= ", fk_user_modif = ".$user->id; + $sql .= ", usage_opportunity = ".($this->usage_opportunity ? 1 : 0); + $sql .= ", usage_task = ".($this->usage_task ? 1 : 0); + $sql .= ", usage_bill_time = ".($this->usage_bill_time ? 1 : 0); + $sql .= ", usage_organize_event = ".($this->usage_organize_event ? 1 : 0); + $sql .= " WHERE rowid = ".$this->id; - dol_syslog(get_class($this)."::update", LOG_DEBUG); - $resql = $this->db->query($sql); - if ($resql) - { - // Update extrafield - if (!$error) - { - if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used - { - $result = $this->insertExtraFields(); - if ($result < 0) - { - $error++; - } - } - } + dol_syslog(get_class($this)."::update", LOG_DEBUG); + $resql = $this->db->query($sql); + if ($resql) + { + // Update extrafield + if (!$error) + { + if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used + { + $result = $this->insertExtraFields(); + if ($result < 0) + { + $error++; + } + } + } - if (!$error && !$notrigger) - { - // Call trigger - $result = $this->call_trigger('PROJECT_MODIFY', $user); - if ($result < 0) { $error++; } - // End call triggers - } + if (!$error && !$notrigger) + { + // Call trigger + $result = $this->call_trigger('PROJECT_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->projet->dir_output) - { - $olddir = $conf->projet->dir_output."/".dol_sanitizeFileName($this->oldcopy->ref); - $newdir = $conf->projet->dir_output."/".dol_sanitizeFileName($this->ref); - if (file_exists($olddir)) - { + if (!$error && (is_object($this->oldcopy) && $this->oldcopy->ref !== $this->ref)) + { + // We remove directory + if ($conf->projet->dir_output) + { + $olddir = $conf->projet->dir_output."/".dol_sanitizeFileName($this->oldcopy->ref); + $newdir = $conf->projet->dir_output."/".dol_sanitizeFileName($this->ref); + if (file_exists($olddir)) + { include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; $res = @rename($olddir, $newdir); if (!$res) - { - $langs->load("errors"); + { + $langs->load("errors"); $this->error = $langs->trans('ErrorFailToRenameDir', $olddir, $newdir); - $error++; - } - } - } - } - if (!$error) - { - $this->db->commit(); - $result = 1; - } - else + $error++; + } + } + } + } + if (!$error) { - $this->db->rollback(); - $result = -1; - } - } - else + $this->db->commit(); + $result = 1; + } + else + { + $this->db->rollback(); + $result = -1; + } + } + else { - $this->error = $this->db->lasterror(); - $this->errors[] = $this->error; - $this->db->rollback(); - if ($this->db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') - { - $result = -4; - } - else - { - $result = -2; - } - dol_syslog(get_class($this)."::update error ".$result." ".$this->error, LOG_ERR); - } - } - else - { - dol_syslog(get_class($this)."::update ref null"); - $result = -1; - } + $this->error = $this->db->lasterror(); + $this->errors[] = $this->error; + $this->db->rollback(); + if ($this->db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') + { + $result = -4; + } + else + { + $result = -2; + } + dol_syslog(get_class($this)."::update error ".$result." ".$this->error, LOG_ERR); + } + } + else + { + dol_syslog(get_class($this)."::update ref null"); + $result = -1; + } - return $result; - } + return $result; + } - /** - * Get object from database - * - * @param int $id Id of object to load - * @param string $ref Ref of project - * @return int >0 if OK, 0 if not found, <0 if KO - */ - public function fetch($id, $ref = '') - { - global $conf; + /** + * Get object from database + * + * @param int $id Id of object to load + * @param string $ref Ref of project + * @return int >0 if OK, 0 if not found, <0 if KO + */ + public function fetch($id, $ref = '') + { + global $conf; - if (empty($id) && empty($ref)) return -1; + if (empty($id) && empty($ref)) return -1; - $sql = "SELECT rowid, ref, title, description, public, datec, opp_amount, budget_amount,"; - $sql .= " tms, dateo, datee, date_close, fk_soc, fk_user_creat, fk_user_modif, fk_user_close, fk_statut, fk_opp_status, opp_percent,"; - $sql .= " note_private, note_public, model_pdf, usage_opportunity, usage_task, usage_bill_time, usage_organize_event, entity"; - $sql .= " FROM ".MAIN_DB_PREFIX."projet"; - if (!empty($id)) - { - $sql .= " WHERE rowid=".$id; - } - elseif (!empty($ref)) - { - $sql .= " WHERE ref='".$this->db->escape($ref)."'"; - $sql .= " AND entity IN (".getEntity('project').")"; - } + $sql = "SELECT rowid, ref, title, description, public, datec, opp_amount, budget_amount,"; + $sql .= " tms, dateo, datee, date_close, fk_soc, fk_user_creat, fk_user_modif, fk_user_close, fk_statut, fk_opp_status, opp_percent,"; + $sql .= " note_private, note_public, model_pdf, usage_opportunity, usage_task, usage_bill_time, usage_organize_event, entity"; + $sql .= " FROM ".MAIN_DB_PREFIX."projet"; + if (!empty($id)) + { + $sql .= " WHERE rowid=".$id; + } + elseif (!empty($ref)) + { + $sql .= " WHERE ref='".$this->db->escape($ref)."'"; + $sql .= " AND entity IN (".getEntity('project').")"; + } - dol_syslog(get_class($this)."::fetch", LOG_DEBUG); - $resql = $this->db->query($sql); - if ($resql) - { - $num_rows = $this->db->num_rows($resql); + 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); + if ($num_rows) + { + $obj = $this->db->fetch_object($resql); - $this->id = $obj->rowid; - $this->ref = $obj->ref; - $this->title = $obj->title; - $this->description = $obj->description; - $this->date_c = $this->db->jdate($obj->datec); - $this->datec = $this->db->jdate($obj->datec); // TODO deprecated - $this->date_m = $this->db->jdate($obj->tms); - $this->datem = $this->db->jdate($obj->tms); // TODO deprecated - $this->date_start = $this->db->jdate($obj->dateo); - $this->date_end = $this->db->jdate($obj->datee); - $this->date_close = $this->db->jdate($obj->date_close); - $this->note_private = $obj->note_private; - $this->note_public = $obj->note_public; - $this->socid = $obj->fk_soc; - $this->user_author_id = $obj->fk_user_creat; - $this->user_modification_id = $obj->fk_user_modif; - $this->user_close_id = $obj->fk_user_close; - $this->public = $obj->public; - $this->statut = $obj->fk_statut; - $this->opp_status = $obj->fk_opp_status; - $this->opp_amount = $obj->opp_amount; - $this->opp_percent = $obj->opp_percent; - $this->budget_amount = $obj->budget_amount; - $this->modelpdf = $obj->model_pdf; - $this->usage_opportunity = (int) $obj->usage_opportunity; - $this->usage_task = (int) $obj->usage_task; - $this->usage_bill_time = (int) $obj->usage_bill_time; - $this->usage_organize_event = (int) $obj->usage_organize_event; - $this->entity = $obj->entity; + $this->id = $obj->rowid; + $this->ref = $obj->ref; + $this->title = $obj->title; + $this->description = $obj->description; + $this->date_c = $this->db->jdate($obj->datec); + $this->datec = $this->db->jdate($obj->datec); // TODO deprecated + $this->date_m = $this->db->jdate($obj->tms); + $this->datem = $this->db->jdate($obj->tms); // TODO deprecated + $this->date_start = $this->db->jdate($obj->dateo); + $this->date_end = $this->db->jdate($obj->datee); + $this->date_close = $this->db->jdate($obj->date_close); + $this->note_private = $obj->note_private; + $this->note_public = $obj->note_public; + $this->socid = $obj->fk_soc; + $this->user_author_id = $obj->fk_user_creat; + $this->user_modification_id = $obj->fk_user_modif; + $this->user_close_id = $obj->fk_user_close; + $this->public = $obj->public; + $this->statut = $obj->fk_statut; + $this->opp_status = $obj->fk_opp_status; + $this->opp_amount = $obj->opp_amount; + $this->opp_percent = $obj->opp_percent; + $this->budget_amount = $obj->budget_amount; + $this->modelpdf = $obj->model_pdf; + $this->usage_opportunity = (int) $obj->usage_opportunity; + $this->usage_task = (int) $obj->usage_task; + $this->usage_bill_time = (int) $obj->usage_bill_time; + $this->usage_organize_event = (int) $obj->usage_organize_event; + $this->entity = $obj->entity; - $this->db->free($resql); + $this->db->free($resql); - // Retreive all extrafield - // fetch optionals attributes and labels - $this->fetch_optionals(); + // Retreive all extrafield + // fetch optionals attributes and labels + $this->fetch_optionals(); - return 1; - } + return 1; + } - $this->db->free($resql); + $this->db->free($resql); - return 0; - } - else - { - $this->error = $this->db->lasterror(); - return -1; - } - } + return 0; + } + else + { + $this->error = $this->db->lasterror(); + return -1; + } + } - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Return list of elements for type, linked to a project - * - * @param string $type 'propal','order','invoice','order_supplier','invoice_supplier',... - * @param string $tablename name of table associated of the type - * @param string $datefieldname name of date field for filter - * @param int $dates Start date - * @param int $datee End date + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return list of elements for type, linked to a project + * + * @param string $type 'propal','order','invoice','order_supplier','invoice_supplier',... + * @param string $tablename name of table associated of the type + * @param string $datefieldname name of date field for filter + * @param int $dates Start date + * @param int $datee End date * @param string $projectkey Equivalent key to fk_projet for actual type - * @return mixed Array list of object ids linked to project, < 0 or string if error - */ - public function get_element_list($type, $tablename, $datefieldname = '', $dates = '', $datee = '', $projectkey = 'fk_projet') - { - // phpcs:enable - $elements = array(); + * @return mixed Array list of object ids linked to project, < 0 or string if error + */ + public function get_element_list($type, $tablename, $datefieldname = '', $dates = '', $datee = '', $projectkey = 'fk_projet') + { + // phpcs:enable + $elements = array(); - if ($this->id <= 0) return $elements; + if ($this->id <= 0) return $elements; - $ids = $this->id; + $ids = $this->id; if ($type == 'agenda') - { - $sql = "SELECT id as rowid FROM ".MAIN_DB_PREFIX."actioncomm WHERE fk_project IN (".$ids.") AND entity IN (".getEntity('agenda').")"; - } - elseif ($type == 'expensereport') { - $sql = "SELECT ed.rowid FROM ".MAIN_DB_PREFIX."expensereport as e, ".MAIN_DB_PREFIX."expensereport_det as ed WHERE e.rowid = ed.fk_expensereport AND e.entity IN (".getEntity('expensereport').") AND ed.fk_projet IN (".$ids.")"; + $sql = "SELECT id as rowid FROM ".MAIN_DB_PREFIX."actioncomm WHERE fk_project IN (".$ids.") AND entity IN (".getEntity('agenda').")"; } - elseif ($type == 'project_task') + elseif ($type == 'expensereport') + { + $sql = "SELECT ed.rowid FROM ".MAIN_DB_PREFIX."expensereport as e, ".MAIN_DB_PREFIX."expensereport_det as ed WHERE e.rowid = ed.fk_expensereport AND e.entity IN (".getEntity('expensereport').") AND ed.fk_projet IN (".$ids.")"; + } + elseif ($type == 'project_task') { $sql = "SELECT DISTINCT pt.rowid FROM ".MAIN_DB_PREFIX."projet_task as pt WHERE pt.fk_projet IN (".$ids.")"; } @@ -607,18 +607,18 @@ class Project extends CommonObject { $sql = 'SELECT ms.rowid, ms.fk_user_author as fk_user FROM '.MAIN_DB_PREFIX."stock_mouvement as ms, ".MAIN_DB_PREFIX."entrepot as e WHERE e.rowid = ms.fk_entrepot AND e.entity IN (".getEntity('stock').") AND ms.origintype = 'project' AND ms.fk_origin IN (".$ids.") AND ms.type_mouvement = 1"; } - elseif ($type == 'loan') - { - $sql = 'SELECT l.rowid, l.fk_user_author as fk_user FROM '.MAIN_DB_PREFIX."loan as l WHERE l.entity IN (".getEntity('loan').") AND l.fk_projet IN (".$ids.")"; - } - else + elseif ($type == 'loan') { - $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX.$tablename." WHERE ".$projectkey." IN (".$ids.") AND entity IN (".getEntity($type).")"; - } + $sql = 'SELECT l.rowid, l.fk_user_author as fk_user FROM '.MAIN_DB_PREFIX."loan as l WHERE l.entity IN (".getEntity('loan').") AND l.fk_projet IN (".$ids.")"; + } + else + { + $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX.$tablename." WHERE ".$projectkey." IN (".$ids.") AND entity IN (".getEntity($type).")"; + } - if($dates > 0 && $type == 'loan'){ - $sql .= " AND (dateend > '".$this->db->idate($dates)."' OR dateend IS NULL)"; - } + if($dates > 0 && $type == 'loan'){ + $sql .= " AND (dateend > '".$this->db->idate($dates)."' OR dateend IS NULL)"; + } elseif ($dates > 0 && ($type != 'project_task')) // For table project_taks, we want the filter on date apply on project_time_spent table { if (empty($datefieldname) && !empty($this->table_element_date)) $datefieldname = $this->table_element_date; @@ -626,10 +626,10 @@ class Project extends CommonObject $sql .= " AND (".$datefieldname." >= '".$this->db->idate($dates)."' OR ".$datefieldname." IS NULL)"; } - if($datee > 0 && $type == 'loan'){ - $sql .= " AND (datestart < '".$this->db->idate($datee)."' OR datestart IS NULL)"; - } - elseif ($datee > 0 && ($type != 'project_task')) // For table project_taks, we want the filter on date apply on project_time_spent table + if($datee > 0 && $type == 'loan'){ + $sql .= " AND (datestart < '".$this->db->idate($datee)."' OR datestart IS NULL)"; + } + elseif ($datee > 0 && ($type != 'project_task')) // For table project_taks, we want the filter on date apply on project_time_spent table { if (empty($datefieldname) && !empty($this->table_element_date)) $datefieldname = $this->table_element_date; if (empty($datefieldname)) return 'Error this object has no date field defined'; @@ -637,94 +637,94 @@ class Project extends CommonObject } if (!$sql) return -1; - //print $sql; - dol_syslog(get_class($this)."::get_element_list", LOG_DEBUG); - $result = $this->db->query($sql); - if ($result) - { - $nump = $this->db->num_rows($result); - if ($nump) - { - $i = 0; - while ($i < $nump) - { - $obj = $this->db->fetch_object($result); + //print $sql; + dol_syslog(get_class($this)."::get_element_list", LOG_DEBUG); + $result = $this->db->query($sql); + if ($result) + { + $nump = $this->db->num_rows($result); + if ($nump) + { + $i = 0; + while ($i < $nump) + { + $obj = $this->db->fetch_object($result); - $elements[$i] = $obj->rowid.(empty($obj->fk_user) ? '' : '_'.$obj->fk_user); + $elements[$i] = $obj->rowid.(empty($obj->fk_user) ? '' : '_'.$obj->fk_user); - $i++; - } - $this->db->free($result); - } + $i++; + } + $this->db->free($result); + } - /* Return array even if empty*/ - return $elements; - } - else - { - dol_print_error($this->db); - } - } + /* Return array even if empty*/ + return $elements; + } + else + { + dol_print_error($this->db); + } + } - /** - * Delete a project from database - * - * @param User $user User - * @param int $notrigger Disable triggers - * @return int <0 if KO, 0 if not possible, >0 if OK - */ - public function delete($user, $notrigger = 0) - { - global $langs, $conf; - require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; + /** + * Delete a project from database + * + * @param User $user User + * @param int $notrigger Disable triggers + * @return int <0 if KO, 0 if not possible, >0 if OK + */ + public function delete($user, $notrigger = 0) + { + global $langs, $conf; + require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; - $error = 0; + $error = 0; - $this->db->begin(); + $this->db->begin(); - if (!$error) - { - // Delete linked contacts - $res = $this->delete_linked_contact(); - if ($res < 0) - { - $this->error = 'ErrorFailToDeleteLinkedContact'; - //$error++; - $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; + } + } - // Set fk_projet into elements to null - $listoftables = array( - 'propal'=>'fk_projet', 'commande'=>'fk_projet', 'facture'=>'fk_projet', - 'supplier_proposal'=>'fk_projet', 'commande_fournisseur'=>'fk_projet', 'facture_fourn'=>'fk_projet', - 'expensereport_det'=>'fk_projet', 'contrat'=>'fk_projet', 'fichinter'=>'fk_projet', 'don'=>'fk_projet', - 'actioncomm'=>'fk_project' - ); - foreach ($listoftables as $key => $value) - { - $sql = "UPDATE ".MAIN_DB_PREFIX.$key." SET ".$value." = NULL where ".$value." = ".$this->id; - $resql = $this->db->query($sql); - if (!$resql) - { - $this->errors[] = $this->db->lasterror(); - $error++; - break; - } - } + // Set fk_projet into elements to null + $listoftables = array( + 'propal'=>'fk_projet', 'commande'=>'fk_projet', 'facture'=>'fk_projet', + 'supplier_proposal'=>'fk_projet', 'commande_fournisseur'=>'fk_projet', 'facture_fourn'=>'fk_projet', + 'expensereport_det'=>'fk_projet', 'contrat'=>'fk_projet', 'fichinter'=>'fk_projet', 'don'=>'fk_projet', + 'actioncomm'=>'fk_project' + ); + foreach ($listoftables as $key => $value) + { + $sql = "UPDATE ".MAIN_DB_PREFIX.$key." SET ".$value." = NULL where ".$value." = ".$this->id; + $resql = $this->db->query($sql); + if (!$resql) + { + $this->errors[] = $this->db->lasterror(); + $error++; + break; + } + } - // Remove linked categories. - if (!$error) { - $sql = "DELETE FROM ".MAIN_DB_PREFIX."categorie_project"; - $sql .= " WHERE fk_project = ".$this->id; + // Remove linked categories. + if (!$error) { + $sql = "DELETE FROM ".MAIN_DB_PREFIX."categorie_project"; + $sql .= " WHERE fk_project = ".$this->id; - $result = $this->db->query($sql); - if (!$result) { - $error++; - $this->errors[] = $this->db->lasterror(); - } - } + $result = $this->db->query($sql); + if (!$result) { + $error++; + $this->errors[] = $this->db->lasterror(); + } + } // Fetch tasks $this->getLinesArray($user); @@ -754,1334 +754,1333 @@ class Project extends CommonObject - // Delete project - if (!$error) - { - $sql = "DELETE FROM ".MAIN_DB_PREFIX."projet"; - $sql .= " WHERE rowid=".$this->id; + // Delete project + if (!$error) + { + $sql = "DELETE FROM ".MAIN_DB_PREFIX."projet"; + $sql .= " WHERE rowid=".$this->id; - $resql = $this->db->query($sql); - if (!$resql) - { - $this->errors[] = $langs->trans("CantRemoveProject"); - $error++; - } - } + $resql = $this->db->query($sql); + if (!$resql) + { + $this->errors[] = $langs->trans("CantRemoveProject"); + $error++; + } + } - if (!$error) - { - $sql = "DELETE FROM ".MAIN_DB_PREFIX."projet_extrafields"; - $sql .= " WHERE fk_object=".$this->id; + if (!$error) + { + $sql = "DELETE FROM ".MAIN_DB_PREFIX."projet_extrafields"; + $sql .= " WHERE fk_object=".$this->id; - $resql = $this->db->query($sql); - if (!$resql) - { - $this->errors[] = $this->db->lasterror(); - $error++; - } - } + $resql = $this->db->query($sql); + if (!$resql) + { + $this->errors[] = $this->db->lasterror(); + $error++; + } + } - if (empty($error)) { - // We remove directory - $projectref = dol_sanitizeFileName($this->ref); - if ($conf->projet->dir_output) { - $dir = $conf->projet->dir_output."/".$projectref; - if (file_exists($dir)) { - $res = @dol_delete_dir_recursive($dir); - if (!$res) { - $this->errors[] = 'ErrorFailToDeleteDir'; - $error++; - } - } - } + if (empty($error)) { + // We remove directory + $projectref = dol_sanitizeFileName($this->ref); + if ($conf->projet->dir_output) { + $dir = $conf->projet->dir_output."/".$projectref; + if (file_exists($dir)) { + $res = @dol_delete_dir_recursive($dir); + if (!$res) { + $this->errors[] = 'ErrorFailToDeleteDir'; + $error++; + } + } + } - if (!$notrigger) - { - // Call trigger - $result = $this->call_trigger('PROJECT_DELETE', $user); + if (!$notrigger) + { + // Call trigger + $result = $this->call_trigger('PROJECT_DELETE', $user); - if ($result < 0) { - $error++; - } - // End call triggers - } - } + if ($result < 0) { + $error++; + } + // End call triggers + } + } - if (empty($error)) - { - $this->db->commit(); - return 1; - } - else - { - foreach ($this->errors as $errmsg) - { + if (empty($error)) + { + $this->db->commit(); + return 1; + } + else + { + foreach ($this->errors as $errmsg) + { dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR); $this->error .= ($this->error ? ', '.$errmsg : $errmsg); } - dol_syslog(get_class($this)."::delete ".$this->error, LOG_ERR); - $this->db->rollback(); - return -1; - } - } - - /** - * Delete tasks with no children first, then task with children recursively - * - * @param User $user User - * @return int <0 if KO, 1 if OK - */ - public function deleteTasks($user) - { - $countTasks = count($this->lines); - $deleted = false; - if ($countTasks) - { - foreach ($this->lines as $task) - { - if ($task->hasChildren() <= 0) { // If there is no children (or error to detect them) - $deleted = true; - $ret = $task->delete($user); - if ($ret <= 0) - { - $this->errors[] = $this->db->lasterror(); - return -1; - } - } - } - } - $this->getLinesArray($user); - if ($deleted && count($this->lines) < $countTasks) - { - if (count($this->lines)) $this->deleteTasks($this->lines); - } - - return 1; - } - - /** - * Validate a project - * - * @param User $user User that validate - * @param int $notrigger 1=Disable triggers - * @return int <0 if KO, >0 if OK - */ - public function setValid($user, $notrigger = 0) - { - global $langs, $conf; - - $error = 0; - - if ($this->statut != 1) - { - // Check parameters - if (preg_match('/^'.preg_quote($langs->trans("CopyOf").' ').'/', $this->title)) - { - $this->error = $langs->trans("ErrorFieldFormat", $langs->transnoentities("Label")).'. '.$langs->trans('RemoveString', $langs->transnoentitiesnoconv("CopyOf")); - return -1; - } - - $this->db->begin(); - - $sql = "UPDATE ".MAIN_DB_PREFIX."projet"; - $sql .= " SET fk_statut = 1"; - $sql .= " WHERE rowid = ".$this->id; - $sql .= " AND entity = ".$conf->entity; - - dol_syslog(get_class($this)."::setValid", LOG_DEBUG); - $resql = $this->db->query($sql); - if ($resql) - { - // Call trigger - if (empty($notrigger)) - { - $result = $this->call_trigger('PROJECT_VALIDATE', $user); - if ($result < 0) { $error++; } - // End call triggers - } - - if (!$error) - { - $this->statut = 1; - $this->db->commit(); - return 1; - } - else - { - $this->db->rollback(); - $this->error = join(',', $this->errors); - dol_syslog(get_class($this)."::setValid ".$this->error, LOG_ERR); - return -1; - } - } - else - { - $this->db->rollback(); - $this->error = $this->db->lasterror(); - return -1; - } - } - } - - /** - * Close a project - * - * @param User $user User that close project - * @return int <0 if KO, 0 if already closed, >0 if OK - */ - public function setClose($user) - { - global $langs, $conf; - - $now = dol_now(); - - $error = 0; - - if ($this->statut != 2) - { - $this->db->begin(); - - $sql = "UPDATE ".MAIN_DB_PREFIX."projet"; - $sql .= " SET fk_statut = 2, fk_user_close = ".$user->id.", date_close = '".$this->db->idate($now)."'"; - $sql .= " WHERE rowid = ".$this->id; - $sql .= " AND entity = ".$conf->entity; - $sql .= " AND fk_statut = 1"; - - if (!empty($conf->global->PROJECT_USE_OPPORTUNITIES)) - { - // TODO What to do if fk_opp_status is not code 'WON' or 'LOST' - } - - dol_syslog(get_class($this)."::setClose", LOG_DEBUG); - $resql = $this->db->query($sql); - if ($resql) - { - // Call trigger - $result = $this->call_trigger('PROJECT_CLOSE', $user); - if ($result < 0) { $error++; } - // End call triggers - - if (!$error) - { - $this->statut = 2; - $this->db->commit(); - return 1; - } - else - { - $this->db->rollback(); - $this->error = join(',', $this->errors); - dol_syslog(get_class($this)."::setClose ".$this->error, LOG_ERR); - return -1; - } - } - else - { - $this->db->rollback(); - $this->error = $this->db->lasterror(); - return -1; - } - } - - return 0; - } - - /** - * Return status label of object - * - * @param int $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->statut, $mode); - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Renvoi status label for a status - * - * @param int $status id status - * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto - * @return string Label - */ - public function LibStatut($status, $mode = 0) - { - // phpcs:enable - global $langs; - - $statustrans = array( - 0 => 'status0', - 1 => 'status4', - 2 => 'status6', - ); - - $statusClass = 'status0'; - if (!empty($statustrans[$status])) { - $statusClass = $statustrans[$status]; - } - - return dolGetStatus($langs->trans($this->statuts_long[$status]), $langs->trans($this->statuts_short[$status]), '', $statusClass, $mode); - } - - /** - * Return clicable name (with picto eventually) - * - * @param int $withpicto 0=No picto, 1=Include picto into link, 2=Only picto - * @param string $option Variant ('', 'nolink') - * @param int $addlabel 0=Default, 1=Add label into string, >1=Add first chars into string - * @param string $moreinpopup Text to add into popup - * @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 String with URL - */ - public function getNomUrl($withpicto = 0, $option = '', $addlabel = 0, $moreinpopup = '', $sep = ' - ', $notooltip = 0, $save_lastsearch_value = -1) - { - global $conf, $langs, $user, $hookmanager; - - if (!empty($conf->dol_no_mouse_hover)) $notooltip = 1; // Force disable tooltips - - $result = ''; - - $label = ''; - if ($option != 'nolink') $label = ''.$langs->trans("ShowProject").''; - $label .= ($label ? '
' : '').''.$langs->trans('Ref').': '.$this->ref; // The space must be after the : to not being explode when showing the title in img_picto - $label .= ($label ? '
' : '').''.$langs->trans('Label').': '.$this->title; // The space must be after the : to not being explode when showing the title in img_picto - if (!empty($this->thirdparty_name)) - $label .= ($label ? '
' : '').''.$langs->trans('ThirdParty').': '.$this->thirdparty_name; // The space must be after the : to not being explode when showing the title in img_picto - if (!empty($this->dateo)) - $label .= ($label ? '
' : '').''.$langs->trans('DateStart').': '.dol_print_date($this->dateo, 'day'); // The space must be after the : to not being explode when showing the title in img_picto - if (!empty($this->datee)) - $label .= ($label ? '
' : '').''.$langs->trans('DateEnd').': '.dol_print_date($this->datee, 'day'); // The space must be after the : to not being explode when showing the title in img_picto - if ($moreinpopup) $label .= '
'.$moreinpopup; - - $url = ''; - if ($option != 'nolink') - { - if (preg_match('/\.php$/', $option)) { - $url = dol_buildpath($option, 1).'?id='.$this->id; - } - elseif ($option == 'task') - { - $url = DOL_URL_ROOT.'/projet/tasks.php?id='.$this->id; - } - else - { - $url = DOL_URL_ROOT.'/projet/card.php?id='.$this->id; - } - // Add param to save lastsearch_values or not - $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0); - if ($save_lastsearch_value == -1 && 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) && $user->rights->projet->lire) - { - if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) - { - $label = $langs->trans("ShowProject"); - $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"'; - } - $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"'; - $linkclose .= ' class="classfortooltip"'; - - /* - $hookmanager->initHooks(array('projectdao')); - $parameters=array('id'=>$this->id); - // Note that $action and $object may have been modified by some hooks - $reshook=$hookmanager->executeHooks('getnomurltooltip',$parameters,$this,$action); - if ($reshook > 0) - $linkclose = $hookmanager->resPrint; - */ - } - - $picto = 'projectpub'; - if (!$this->public) $picto = 'project'; - - $linkstart = ''; - $linkend = ''; - - $result .= $linkstart; - if ($withpicto) $result .= img_object(($notooltip ? '' : $label), $picto, ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip ? 0 : 1); - if ($withpicto != 2) $result .= $this->ref; - $result .= $linkend; - if ($withpicto != 2) $result .= (($addlabel && $this->title) ? $sep.dol_trunc($this->title, ($addlabel > 1 ? $addlabel : 0)) : ''); - - global $action; - $hookmanager->initHooks(array('projectdao')); - $parameters = array('id'=>$this->id, 'getnomurl'=>$result); - $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks - if ($reshook > 0) $result = $hookmanager->resPrint; - else $result .= $hookmanager->resPrint; - - 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() - { - global $user, $langs, $conf; - - $now = dol_now(); - - // Initialise parameters - $this->id = 0; - $this->ref = 'SPECIMEN'; - $this->specimen = 1; - $this->socid = 1; - $this->date_c = $now; - $this->date_m = $now; - $this->date_start = $now; - $this->date_end = $now + (3600 * 24 * 365); - $this->note_public = 'SPECIMEN'; - $this->fk_ele = 20000; - $this->opp_amount = 20000; - $this->budget_amount = 10000; - - $this->usage_opportunity = 1; - $this->usage_task = 1; - $this->usage_bill_time = 1; - $this->usage_organize_event = 1; - - /* - $nbp = mt_rand(1, 9); - $xnbp = 0; - while ($xnbp < $nbp) - { - $line = new Task($this->db); - $line->fk_project = 0; - $line->label = $langs->trans("Label") . " " . $xnbp; - $line->description = $langs->trans("Description") . " " . $xnbp; - - $this->lines[]=$line; - $xnbp++; - } - */ - } - - /** - * Check if user has permission on current project - * - * @param User $user Object user to evaluate - * @param string $mode Type of permission we want to know: 'read', 'write' - * @return int >0 if user has permission, <0 if user has no permission - */ - public function restrictedProjectArea($user, $mode = 'read') - { - // To verify role of users - $userAccess = 0; - if (($mode == 'read' && !empty($user->rights->projet->all->lire)) || ($mode == 'write' && !empty($user->rights->projet->all->creer)) || ($mode == 'delete' && !empty($user->rights->projet->all->supprimer))) - { - $userAccess = 1; - } - elseif ($this->public && (($mode == 'read' && !empty($user->rights->projet->lire)) || ($mode == 'write' && !empty($user->rights->projet->creer)) || ($mode == 'delete' && !empty($user->rights->projet->supprimer)))) - { - $userAccess = 1; - } - else - { - foreach (array('internal', 'external') as $source) - { - $userRole = $this->liste_contact(4, $source); - $num = count($userRole); - - $nblinks = 0; - while ($nblinks < $num) - { - if ($source == 'internal' && preg_match('/^PROJECT/', $userRole[$nblinks]['code']) && $user->id == $userRole[$nblinks]['id']) - { - if ($mode == 'read' && $user->rights->projet->lire) $userAccess++; - if ($mode == 'write' && $user->rights->projet->creer) $userAccess++; - if ($mode == 'delete' && $user->rights->projet->supprimer) $userAccess++; - } - $nblinks++; - } - } - //if (empty($nblinks)) // If nobody has permission, we grant creator - //{ - // if ((!empty($this->user_author_id) && $this->user_author_id == $user->id)) - // { - // $userAccess = 1; - // } - //} - } - - return ($userAccess ? $userAccess : -1); - } - - /** - * Return array of projects a user has permission on, is affected to, or all projects - * - * @param User $user User object - * @param int $mode 0=All project I have permission on (assigned to me or public), 1=Projects assigned to me only, 2=Will return list of all projects with no test on contacts - * @param int $list 0=Return array, 1=Return string list - * @param int $socid 0=No filter on third party, id of third party - * @param string $filter additionnal filter on project (statut, ref, ...) - * @return array or string Array of projects id, or string with projects id separated with "," if list is 1 - */ - public function getProjectsAuthorizedForUser($user, $mode = 0, $list = 0, $socid = 0, $filter = '') - { - $projects = array(); - $temp = array(); - - $sql = "SELECT ".(($mode == 0 || $mode == 1) ? "DISTINCT " : "")."p.rowid, p.ref"; - $sql.= " FROM " . MAIN_DB_PREFIX . "projet as p"; - if ($mode == 0) - { - $sql.= " LEFT JOIN " . MAIN_DB_PREFIX . "element_contact as ec ON ec.element_id = p.rowid"; - } - elseif ($mode == 1) - { - $sql.= ", " . MAIN_DB_PREFIX . "element_contact as ec"; - } - elseif ($mode == 2) - { - // No filter. Use this if user has permission to see all project - } - $sql.= " WHERE p.entity IN (".getEntity('project').")"; - // Internal users must see project he is contact to even if project linked to a third party he can't see. - //if ($socid || ! $user->rights->societe->client->voir) $sql.= " AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = ".$socid.")"; - if ($socid > 0) $sql.= " AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = " . $socid . ")"; - - // Get id of types of contacts for projects (This list never contains a lot of elements) - $listofprojectcontacttype=array(); - $sql2 = "SELECT ctc.rowid, ctc.code FROM ".MAIN_DB_PREFIX."c_type_contact as ctc"; - $sql2.= " WHERE ctc.element = '" . $this->db->escape($this->element) . "'"; - $sql2.= " AND ctc.source = 'internal'"; - $resql = $this->db->query($sql2); - if ($resql) - { - while($obj = $this->db->fetch_object($resql)) - { - $listofprojectcontacttype[$obj->rowid]=$obj->code; - } - } - else dol_print_error($this->db); - if (count($listofprojectcontacttype) == 0) $listofprojectcontacttype[0]='0'; // To avoid syntax error if not found - - if ($mode == 0) - { - $sql.= " AND ( p.public = 1"; - $sql.= " OR ( ec.fk_c_type_contact IN (".join(',', array_keys($listofprojectcontacttype)).")"; - $sql.= " AND ec.fk_socpeople = ".$user->id.")"; - $sql.= " )"; - } - elseif ($mode == 1) - { - $sql.= " AND ec.element_id = p.rowid"; - $sql.= " AND ("; - $sql.= " ( ec.fk_c_type_contact IN (".join(',', array_keys($listofprojectcontacttype)).")"; - $sql.= " AND ec.fk_socpeople = ".$user->id.")"; - $sql.= " )"; - } - elseif ($mode == 2) - { - // No filter. Use this if user has permission to see all project - } - - $sql.= $filter; - //print $sql; - - $resql = $this->db->query($sql); - if ($resql) - { - $num = $this->db->num_rows($resql); - $i = 0; - while ($i < $num) - { - $row = $this->db->fetch_row($resql); - $projects[$row[0]] = $row[1]; - $temp[] = $row[0]; - $i++; - } - - $this->db->free($resql); - - if ($list) - { - if (empty($temp)) return '0'; - $result = implode(',', $temp); - return $result; - } - } - else - { - dol_print_error($this->db); - } - - return $projects; - } - - /** - * 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 bool $clone_contact Clone contact of project - * @param bool $clone_task Clone task of project - * @param bool $clone_project_file Clone file of project - * @param bool $clone_task_file Clone file of task (if task are copied) - * @param bool $clone_note Clone note of project - * @param bool $move_date Move task date on clone - * @param integer $notrigger No trigger flag - * @param int $newthirdpartyid New thirdparty id - * @return int New id of clone - */ - public function createFromClone(User $user, $fromid, $clone_contact = false, $clone_task = true, $clone_project_file = false, $clone_task_file = false, $clone_note = true, $move_date = true, $notrigger = 0, $newthirdpartyid = 0) - { - global $langs, $conf; - - $error = 0; - - dol_syslog("createFromClone clone_contact=".$clone_contact." clone_task=".$clone_task." clone_project_file=".$clone_project_file." clone_note=".$clone_note." move_date=".$move_date, LOG_DEBUG); - - $now = dol_mktime(0, 0, 0, idate('m', dol_now()), idate('d', dol_now()), idate('Y', dol_now())); - - $clone_project = new Project($this->db); - - $clone_project->context['createfromclone'] = 'createfromclone'; - - $this->db->begin(); - - // Load source object - $clone_project->fetch($fromid); - $clone_project->fetch_optionals(); - if ($newthirdpartyid > 0) $clone_project->socid = $newthirdpartyid; - $clone_project->fetch_thirdparty(); - - $orign_dt_start = $clone_project->date_start; - $orign_project_ref = $clone_project->ref; - - $clone_project->id = 0; - if ($move_date) { - $clone_project->date_start = $now; - if (!(empty($clone_project->date_end))) - { - $clone_project->date_end = $clone_project->date_end + ($now - $orign_dt_start); - } - } - - $clone_project->datec = $now; - - if (!$clone_note) - { - $clone_project->note_private = ''; - $clone_project->note_public = ''; - } - - //Generate next ref - $defaultref = ''; - $obj = empty($conf->global->PROJECT_ADDON) ? 'mod_project_simple' : $conf->global->PROJECT_ADDON; - // Search template files - $file = ''; $classname = ''; $filefound = 0; - $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']); - foreach ($dirmodels as $reldir) - { - $file = dol_buildpath($reldir."core/modules/project/".$obj.'.php', 0); - if (file_exists($file)) - { - $filefound = 1; - dol_include_once($reldir."core/modules/project/".$obj.'.php'); - $modProject = new $obj; - $defaultref = $modProject->getNextValue(is_object($clone_project->thirdparty) ? $clone_project->thirdparty : null, $clone_project); - break; - } - } - if (is_numeric($defaultref) && $defaultref <= 0) $defaultref = ''; - - $clone_project->ref = $defaultref; - $clone_project->title = $langs->trans("CopyOf").' '.$clone_project->title; - - // Create clone - $result = $clone_project->create($user, $notrigger); - - // Other options - if ($result < 0) - { - $this->error .= $clone_project->error; - $error++; - } - - if (!$error) - { - //Get the new project id - $clone_project_id = $clone_project->id; - - //Note Update - if (!$clone_note) - { - $clone_project->note_private = ''; - $clone_project->note_public = ''; - } - else - { - $this->db->begin(); - $res = $clone_project->update_note(dol_html_entity_decode($clone_project->note_public, ENT_QUOTES), '_public'); - if ($res < 0) - { - $this->error .= $clone_project->error; - $error++; - $this->db->rollback(); - } - else - { - $this->db->commit(); - } - - $this->db->begin(); - $res = $clone_project->update_note(dol_html_entity_decode($clone_project->note_private, ENT_QUOTES), '_private'); - if ($res < 0) - { - $this->error .= $clone_project->error; - $error++; - $this->db->rollback(); - } - else - { - $this->db->commit(); - } - } - - //Duplicate contact - if ($clone_contact) - { - $origin_project = new Project($this->db); - $origin_project->fetch($fromid); - - foreach (array('internal', 'external') as $source) - { - $tab = $origin_project->liste_contact(-1, $source); - - foreach ($tab as $contacttoadd) - { - $clone_project->add_contact($contacttoadd['id'], $contacttoadd['code'], $contacttoadd['source'], $notrigger); - if ($clone_project->error == 'DB_ERROR_RECORD_ALREADY_EXISTS') - { - $langs->load("errors"); - $this->error .= $langs->trans("ErrorThisContactIsAlreadyDefinedAsThisType"); - $error++; - } - else - { - if ($clone_project->error != '') - { - $this->error .= $clone_project->error; - $error++; - } - } - } - } - } - - //Duplicate file - if ($clone_project_file) - { - require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; - - $clone_project_dir = $conf->projet->dir_output."/".dol_sanitizeFileName($defaultref); - $ori_project_dir = $conf->projet->dir_output."/".dol_sanitizeFileName($orign_project_ref); - - if (dol_mkdir($clone_project_dir) >= 0) - { - $filearray = dol_dir_list($ori_project_dir, "files", 0, '', '(\.meta|_preview.*\.png)$', '', SORT_ASC, 1); - foreach ($filearray as $key => $file) - { - $rescopy = dol_copy($ori_project_dir.'/'.$file['name'], $clone_project_dir.'/'.$file['name'], 0, 1); - if (is_numeric($rescopy) && $rescopy < 0) - { - $this->error .= $langs->trans("ErrorFailToCopyFile", $ori_project_dir.'/'.$file['name'], $clone_project_dir.'/'.$file['name']); - $error++; - } - } - } - else - { - $this->error .= $langs->trans('ErrorInternalErrorDetected').':dol_mkdir'; - $error++; - } - } - - //Duplicate task - if ($clone_task) - { - require_once DOL_DOCUMENT_ROOT . '/projet/class/task.class.php'; - - $taskstatic = new Task($this->db); - - // Security check - $socid=0; - if ($user->socid > 0) $socid = $user->socid; - - $tasksarray=$taskstatic->getTasksArray(0, 0, $fromid, $socid, 0); - - $tab_conv_child_parent=array(); - - // Loop on each task, to clone it - foreach ($tasksarray as $tasktoclone) - { - $result_clone = $taskstatic->createFromClone($user, $tasktoclone->id, $clone_project_id, $tasktoclone->fk_parent, $move_date, true, false, $clone_task_file, true, false); - if ($result_clone <= 0) - { - $this->error .= $result_clone->error; - $error++; - } - else - { - $new_task_id = $result_clone; - $taskstatic->fetch($tasktoclone->id); - - //manage new parent clone task id - // if the current task has child we store the original task id and the equivalent clone task id - if (($taskstatic->hasChildren()) && !array_key_exists($tasktoclone->id, $tab_conv_child_parent)) - { - $tab_conv_child_parent[$tasktoclone->id] = $new_task_id; - } - } - } - - //Parse all clone node to be sure to update new parent - $tasksarray = $taskstatic->getTasksArray(0, 0, $clone_project_id, $socid, 0); - foreach ($tasksarray as $task_cloned) - { - $taskstatic->fetch($task_cloned->id); - if ($taskstatic->fk_task_parent != 0) - { - $taskstatic->fk_task_parent = $tab_conv_child_parent[$taskstatic->fk_task_parent]; - } - $res = $taskstatic->update($user, $notrigger); - if ($result_clone <= 0) - { - $this->error .= $taskstatic->error; - $error++; - } - } - } - } - - unset($clone_project->context['createfromclone']); - - if (!$error) - { - $this->db->commit(); - return $clone_project_id; - } - else - { + dol_syslog(get_class($this)."::delete ".$this->error, LOG_ERR); $this->db->rollback(); - dol_syslog(get_class($this)."::createFromClone nbError: ".$error." error : ".$this->error, LOG_ERR); return -1; } - } + } - - /** - * Shift project task date from current date to delta + /** + * Delete tasks with no children first, then task with children recursively * - * @param integer $old_project_dt_start Old project start date - * @return int 1 if OK or < 0 if KO + * @param User $user User + * @return int <0 if KO, 1 if OK */ - public function shiftTaskDate($old_project_dt_start) - { - global $user, $langs, $conf; - - $error=0; - - $taskstatic = new Task($this->db); - - // Security check - $socid=0; - if ($user->socid > 0) $socid = $user->socid; - - $tasksarray=$taskstatic->getTasksArray(0, 0, $this->id, $socid, 0); - - foreach ($tasksarray as $tasktoshiftdate) - { - $to_update=false; - // Fetch only if update of date will be made - if ((!empty($tasktoshiftdate->date_start)) || (!empty($tasktoshiftdate->date_end))) - { - //dol_syslog(get_class($this)."::shiftTaskDate to_update", LOG_DEBUG); - $to_update=true; - $task = new Task($this->db); - $result = $task->fetch($tasktoshiftdate->id); - if (!$result) - { - $error++; - $this->error.=$task->error; - } - } - //print "$this->date_start + $tasktoshiftdate->date_start - $old_project_dt_start";exit; - - //Calcultate new task start date with difference between old proj start date and origin task start date - if (!empty($tasktoshiftdate->date_start)) - { - $task->date_start = $this->date_start + ($tasktoshiftdate->date_start - $old_project_dt_start); - } - - //Calcultate new task end date with difference between origin proj end date and origin task end date - if (!empty($tasktoshiftdate->date_end)) - { - $task->date_end = $this->date_start + ($tasktoshiftdate->date_end - $old_project_dt_start); - } - - if ($to_update) + public function deleteTasks($user) + { + $countTasks = count($this->lines); + $deleted = false; + if ($countTasks) + { + foreach ($this->lines as $task) { - $result = $task->update($user); - if (!$result) - { - $error++; - $this->error .= $task->error; - } - } - } - if ($error != 0) - { - return -1; - } - return $result; - } - - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Associate element to a project - * - * @param string $tableName Table of the element to update - * @param int $elementSelectId Key-rowid of the line of the element to update - * @return int 1 if OK or < 0 if KO - */ - public function update_element($tableName, $elementSelectId) - { - // phpcs:enable - $sql = "UPDATE ".MAIN_DB_PREFIX.$tableName; - - if ($tableName == "actioncomm") - { - $sql .= " SET fk_project=".$this->id; - $sql .= " WHERE id=".$elementSelectId; - } - else - { - $sql .= " SET fk_projet=".$this->id; - $sql .= " WHERE rowid=".$elementSelectId; - } - - dol_syslog(get_class($this)."::update_element", LOG_DEBUG); - $resql = $this->db->query($sql); - if (!$resql) { - $this->error = $this->db->lasterror(); - return -1; - } else { - return 1; - } - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Associate element to a project - * - * @param string $tableName Table of the element to update - * @param int $elementSelectId Key-rowid of the line of the element to update - * @param string $projectfield The column name that stores the link with the project - * - * @return int 1 if OK or < 0 if KO - */ - public function remove_element($tableName, $elementSelectId, $projectfield = 'fk_projet') - { - // phpcs:enable - $sql = "UPDATE ".MAIN_DB_PREFIX.$tableName; - - if ($tableName == "actioncomm") - { - $sql .= " SET fk_project=NULL"; - $sql .= " WHERE id=".$elementSelectId; - } else - { - $sql .= " SET ".$projectfield."=NULL"; - $sql .= " WHERE rowid=".$elementSelectId; - } - - dol_syslog(get_class($this)."::remove_element", LOG_DEBUG); - $resql = $this->db->query($sql); - if (!$resql) { - $this->error = $this->db->lasterror(); - return -1; - } else { - return 1; - } - } - - /** - * Create an intervention document on disk using template defined into PROJECT_ADDON_PDF - * - * @param string $modele Force template to use ('' by default) - * @param Translate $outputlangs Objet lang to use for translation - * @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,$langs; - - $langs->load("projects"); - - if (! dol_strlen($modele)) { - $modele = 'baleine'; - - if ($this->modelpdf) { - $modele = $this->modelpdf; - } elseif (! empty($conf->global->PROJECT_ADDON_PDF)) { - $modele = $conf->global->PROJECT_ADDON_PDF; - } - } - - $modelpath = "core/modules/project/doc/"; - - return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref); - } - - - /** - * Load time spent into this->weekWorkLoad and this->weekWorkLoadPerTask for all day of a week of project. - * Note: array weekWorkLoad and weekWorkLoadPerTask are reset and filled at each call. - * - * @param int $datestart First day of week (use dol_get_first_day to find this date) - * @param int $taskid Filter on a task id - * @param int $userid Time spent by a particular user - * @return int <0 if OK, >0 if KO - */ - public function loadTimeSpent($datestart, $taskid = 0, $userid = 0) - { - $error=0; - - $this->weekWorkLoad=array(); - $this->weekWorkLoadPerTask=array(); - - if (empty($datestart)) dol_print_error('', 'Error datestart parameter is empty'); - - $sql = "SELECT ptt.rowid as taskid, ptt.task_duration, ptt.task_date, ptt.task_datehour, ptt.fk_task"; - $sql.= " FROM ".MAIN_DB_PREFIX."projet_task_time AS ptt, ".MAIN_DB_PREFIX."projet_task as pt"; - $sql.= " WHERE ptt.fk_task = pt.rowid"; - $sql.= " AND pt.fk_projet = ".$this->id; - $sql.= " AND (ptt.task_date >= '".$this->db->idate($datestart)."' "; - $sql.= " AND ptt.task_date <= '".$this->db->idate(dol_time_plus_duree($datestart, 1, 'w') - 1)."')"; - if ($taskid) $sql.= " AND ptt.fk_task=".$taskid; - if (is_numeric($userid)) $sql.= " AND ptt.fk_user=".$userid; - - //print $sql; - $resql=$this->db->query($sql); - if ($resql) - { - $daylareadyfound=array(); - - $num = $this->db->num_rows($resql); - $i = 0; - // Loop on each record found, so each couple (project id, task id) - while ($i < $num) - { - $obj=$this->db->fetch_object($resql); - $day=$this->db->jdate($obj->task_date); // task_date is date without hours - if (empty($daylareadyfound[$day])) + if ($task->hasChildren() <= 0) { // If there is no children (or error to detect them) + $deleted = true; + $ret = $task->delete($user); + if ($ret <= 0) { - $this->weekWorkLoad[$day] = $obj->task_duration; - $this->weekWorkLoadPerTask[$day][$obj->fk_task] = $obj->task_duration; + $this->errors[] = $this->db->lasterror(); + return -1; + } } - else - { - $this->weekWorkLoad[$day] += $obj->task_duration; - $this->weekWorkLoadPerTask[$day][$obj->fk_task] += $obj->task_duration; - } - $daylareadyfound[$day]=1; - $i++; } - $this->db->free($resql); - return 1; - } - else - { - $this->error = "Error ".$this->db->lasterror(); - dol_syslog(get_class($this)."::fetch ".$this->error, LOG_ERR); - return -1; - } - } - - - // 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; - - $projectsListId = null; - if (!$user->rights->projet->all->lire) $projectsListId = $this->getProjectsAuthorizedForUser($user, 0, 1); - - $sql = "SELECT p.rowid, p.fk_statut as status, p.fk_opp_status, p.datee as datee"; - $sql .= " FROM (".MAIN_DB_PREFIX."projet as p"; - $sql .= ")"; - $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s on p.fk_soc = s.rowid"; - // For external user, no check is done on company permission because readability is managed by public status of project and assignement. - //if (! $user->rights->societe->client->voir && ! $socid) $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON sc.fk_soc = s.rowid"; - $sql .= " WHERE p.fk_statut = 1"; - $sql .= " AND p.entity IN (".getEntity('project').')'; - if (!empty($projectsListId)) $sql .= " AND p.rowid IN (".$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 = ".$socid.")"; - // For external user, no check is done on company permission because readability is managed by public status of project and assignement. - //if (! $user->rights->societe->client->voir && ! $socid) $sql.= " AND ((s.rowid = sc.fk_soc AND sc.fk_user = " .$user->id.") OR (s.rowid IS NULL))"; - - //print $sql; - $resql = $this->db->query($sql); - if ($resql) - { - $project_static = new Project($this->db); - - $response = new WorkboardResponse(); - $response->warning_delay = $conf->projet->warning_delay / 60 / 60 / 24; - $response->label = $langs->trans("OpenedProjects"); - $response->labelShort = $langs->trans("Opened"); - if ($user->rights->projet->all->lire) $response->url = DOL_URL_ROOT.'/projet/list.php?search_status=1&mainmenu=project'; - else $response->url = DOL_URL_ROOT.'/projet/list.php?search_project_user=-1&search_status=1&mainmenu=project'; - $response->img = img_object('', "projectpub"); - - // This assignment in condition is not a bug. It allows walking the results. - while ($obj = $this->db->fetch_object($resql)) - { - $response->nbtodo++; - - $project_static->statut = $obj->status; - $project_static->opp_status = $obj->opp_status; - $project_static->datee = $this->db->jdate($obj->datee); - - if ($project_static->hasDelay()) { - $response->nbtodolate++; - } - } - - return $response; - } - else - { - $this->error = $this->db->error(); - return -1; - } - } - - - /** - * Function used to replace a thirdparty id with another one. - * - * @param DoliDB $db Database handler - * @param int $origin_id Old thirdparty id - * @param int $dest_id New thirdparty id - * @return bool - */ - public static function replaceThirdparty(DoliDB $db, $origin_id, $dest_id) - { - $tables = array( - 'projet' - ); - - return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables); - } - - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Charge indicateurs this->nb pour le tableau de bord - * - * @return int <0 if KO, >0 if OK - */ - public function load_state_board() - { - // phpcs:enable - global $user; - - $this->nb = array(); - - $sql = "SELECT count(p.rowid) as nb"; - $sql .= " FROM ".MAIN_DB_PREFIX."projet as p"; - $sql .= " WHERE"; - $sql .= " p.entity IN (".getEntity('project').")"; - if (!$user->rights->projet->all->lire) + } + $this->getLinesArray($user); + if ($deleted && count($this->lines) < $countTasks) { - $projectsListId = $this->getProjectsAuthorizedForUser($user, 0, 1); - $sql .= "AND p.rowid IN (".$projectsListId.")"; - } - - $resql = $this->db->query($sql); - if ($resql) - { - while ($obj = $this->db->fetch_object($resql)) - { - $this->nb["projects"] = $obj->nb; - } - $this->db->free($resql); - return 1; - } - else - { - dol_print_error($this->db); - $this->error = $this->db->error(); - return -1; - } - } - - - /** - * Is the project delayed? - * - * @return bool - */ - public function hasDelay() - { - global $conf; - - if (!($this->statut == self::STATUS_VALIDATED)) return false; - if (!$this->datee && !$this->date_end) return false; - - $now = dol_now(); - - return ($this->datee ? $this->datee : $this->date_end) < ($now - $conf->projet->warning_delay); - } - - - /** - * Charge les informations d'ordre info dans l'objet commande - * - * @param int $id Id of order - * @return void - */ - public function info($id) - { - $sql = 'SELECT c.rowid, datec as datec, tms as datem,'; - $sql .= ' date_close as datecloture,'; - $sql .= ' fk_user_creat as fk_user_author, fk_user_close as fk_use_cloture'; - $sql .= ' FROM '.MAIN_DB_PREFIX.'projet as c'; - $sql .= ' WHERE c.rowid = '.$id; - $result = $this->db->query($sql); - if ($result) - { - if ($this->db->num_rows($result)) - { - $obj = $this->db->fetch_object($result); - $this->id = $obj->rowid; - if ($obj->fk_user_author) - { - $cuser = new User($this->db); - $cuser->fetch($obj->fk_user_author); - $this->user_creation = $cuser; - } - - if ($obj->fk_user_cloture) - { - $cluser = new User($this->db); - $cluser->fetch($obj->fk_user_cloture); - $this->user_cloture = $cluser; - } - - $this->date_creation = $this->db->jdate($obj->datec); - $this->date_modification = $this->db->jdate($obj->datem); - $this->date_cloture = $this->db->jdate($obj->datecloture); - } - - $this->db->free($result); - } - else - { - dol_print_error($this->db); - } - } - - /** - * Sets object to supplied categories. - * - * Deletes object from existing categories not supplied. - * Adds it to non existing supplied categories. - * Existing categories are left untouch. - * - * @param int[]|int $categories Category or categories IDs - * @return void - */ - public function setCategories($categories) - { - $type_categ = Categorie::TYPE_PROJECT; - - // Handle single category - if (!is_array($categories)) { - $categories = array($categories); - } - - // Get current categories - require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php'; - $c = new Categorie($this->db); - $existing = $c->containing($this->id, $type_categ, 'id'); - - // Diff - if (is_array($existing)) { - $to_del = array_diff($existing, $categories); - $to_add = array_diff($categories, $existing); - } else { - $to_del = array(); // Nothing to delete - $to_add = $categories; - } - - // Process - foreach ($to_del as $del) { - if ($c->fetch($del) > 0) { - $result = $c->del_type($this, $type_categ); - if ($result < 0) { - $this->errors = $c->errors; - $this->error = $c->error; - return -1; - } - } - } - foreach ($to_add as $add) { - if ($c->fetch($add) > 0) { - $result = $c->add_type($this, $type_categ); - if ($result < 0) { - $this->errors = $c->errors; - $this->error = $c->error; - return -1; - } - } + if (count($this->lines)) $this->deleteTasks($this->lines); } return 1; } + /** + * Validate a project + * + * @param User $user User that validate + * @param int $notrigger 1=Disable triggers + * @return int <0 if KO, >0 if OK + */ + public function setValid($user, $notrigger = 0) + { + global $langs, $conf; + + $error = 0; + + if ($this->statut != 1) + { + // Check parameters + if (preg_match('/^'.preg_quote($langs->trans("CopyOf").' ').'/', $this->title)) + { + $this->error = $langs->trans("ErrorFieldFormat", $langs->transnoentities("Label")).'. '.$langs->trans('RemoveString', $langs->transnoentitiesnoconv("CopyOf")); + return -1; + } + + $this->db->begin(); + + $sql = "UPDATE ".MAIN_DB_PREFIX."projet"; + $sql .= " SET fk_statut = 1"; + $sql .= " WHERE rowid = ".$this->id; + $sql .= " AND entity = ".$conf->entity; + + dol_syslog(get_class($this)."::setValid", LOG_DEBUG); + $resql = $this->db->query($sql); + if ($resql) + { + // Call trigger + if (empty($notrigger)) + { + $result = $this->call_trigger('PROJECT_VALIDATE', $user); + if ($result < 0) { $error++; } + // End call triggers + } + + if (!$error) + { + $this->statut = 1; + $this->db->commit(); + return 1; + } + else + { + $this->db->rollback(); + $this->error = join(',', $this->errors); + dol_syslog(get_class($this)."::setValid ".$this->error, LOG_ERR); + return -1; + } + } + else + { + $this->db->rollback(); + $this->error = $this->db->lasterror(); + return -1; + } + } + } /** - * Create an array of tasks of current project + * Close a project * - * @param User $user Object user we want project allowed to - * @return int >0 if OK, <0 if KO + * @param User $user User that close project + * @return int <0 if KO, 0 if already closed, >0 if OK */ - public function getLinesArray($user) + public function setClose($user) { - require_once DOL_DOCUMENT_ROOT.'/projet/class/task.class.php'; - $taskstatic = new Task($this->db); + global $langs, $conf; - $this->lines = $taskstatic->getTasksArray(0, $user, $this->id, 0, 0); + $now = dol_now(); + + $error = 0; + + if ($this->statut != self::STATUS_CLOSED) + { + $this->db->begin(); + + $sql = "UPDATE ".MAIN_DB_PREFIX."projet"; + $sql .= " SET fk_statut = ".self::STATUS_CLOSED.", fk_user_close = ".$user->id.", date_close = '".$this->db->idate($now)."'"; + $sql .= " WHERE rowid = ".$this->id; + $sql .= " AND fk_statut = ".self::STATUS_VALIDATED; + + if (!empty($conf->global->PROJECT_USE_OPPORTUNITIES)) + { + // TODO What to do if fk_opp_status is not code 'WON' or 'LOST' + } + + dol_syslog(get_class($this)."::setClose", LOG_DEBUG); + $resql = $this->db->query($sql); + if ($resql) + { + // Call trigger + $result = $this->call_trigger('PROJECT_CLOSE', $user); + if ($result < 0) { $error++; } + // End call triggers + + if (!$error) + { + $this->statut = 2; + $this->db->commit(); + return 1; + } + else + { + $this->db->rollback(); + $this->error = join(',', $this->errors); + dol_syslog(get_class($this)."::setClose ".$this->error, LOG_ERR); + return -1; + } + } + else + { + $this->db->rollback(); + $this->error = $this->db->lasterror(); + return -1; + } + } + + return 0; } + + /** + * Return status label of object + * + * @param int $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->statut, $mode); + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Renvoi status label for a status + * + * @param int $status id status + * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto + * @return string Label + */ + public function LibStatut($status, $mode = 0) + { + // phpcs:enable + global $langs; + + $statustrans = array( + 0 => 'status0', + 1 => 'status4', + 2 => 'status6', + ); + + $statusClass = 'status0'; + if (!empty($statustrans[$status])) { + $statusClass = $statustrans[$status]; + } + + return dolGetStatus($langs->trans($this->statuts_long[$status]), $langs->trans($this->statuts_short[$status]), '', $statusClass, $mode); + } + + /** + * Return clicable name (with picto eventually) + * + * @param int $withpicto 0=No picto, 1=Include picto into link, 2=Only picto + * @param string $option Variant ('', 'nolink') + * @param int $addlabel 0=Default, 1=Add label into string, >1=Add first chars into string + * @param string $moreinpopup Text to add into popup + * @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 String with URL + */ + public function getNomUrl($withpicto = 0, $option = '', $addlabel = 0, $moreinpopup = '', $sep = ' - ', $notooltip = 0, $save_lastsearch_value = -1) + { + global $conf, $langs, $user, $hookmanager; + + if (!empty($conf->dol_no_mouse_hover)) $notooltip = 1; // Force disable tooltips + + $result = ''; + + $label = ''; + if ($option != 'nolink') $label = ''.$langs->trans("ShowProject").''; + $label .= ($label ? '
' : '').''.$langs->trans('Ref').': '.$this->ref; // The space must be after the : to not being explode when showing the title in img_picto + $label .= ($label ? '
' : '').''.$langs->trans('Label').': '.$this->title; // The space must be after the : to not being explode when showing the title in img_picto + if (!empty($this->thirdparty_name)) + $label .= ($label ? '
' : '').''.$langs->trans('ThirdParty').': '.$this->thirdparty_name; // The space must be after the : to not being explode when showing the title in img_picto + if (!empty($this->dateo)) + $label .= ($label ? '
' : '').''.$langs->trans('DateStart').': '.dol_print_date($this->dateo, 'day'); // The space must be after the : to not being explode when showing the title in img_picto + if (!empty($this->datee)) + $label .= ($label ? '
' : '').''.$langs->trans('DateEnd').': '.dol_print_date($this->datee, 'day'); // The space must be after the : to not being explode when showing the title in img_picto + if ($moreinpopup) $label .= '
'.$moreinpopup; + + $url = ''; + if ($option != 'nolink') + { + if (preg_match('/\.php$/', $option)) { + $url = dol_buildpath($option, 1).'?id='.$this->id; + } + elseif ($option == 'task') + { + $url = DOL_URL_ROOT.'/projet/tasks.php?id='.$this->id; + } + else + { + $url = DOL_URL_ROOT.'/projet/card.php?id='.$this->id; + } + // Add param to save lastsearch_values or not + $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0); + if ($save_lastsearch_value == -1 && 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) && $user->rights->projet->lire) + { + if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) + { + $label = $langs->trans("ShowProject"); + $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"'; + } + $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"'; + $linkclose .= ' class="classfortooltip"'; + + /* + $hookmanager->initHooks(array('projectdao')); + $parameters=array('id'=>$this->id); + // Note that $action and $object may have been modified by some hooks + $reshook=$hookmanager->executeHooks('getnomurltooltip',$parameters,$this,$action); + if ($reshook > 0) + $linkclose = $hookmanager->resPrint; + */ + } + + $picto = 'projectpub'; + if (!$this->public) $picto = 'project'; + + $linkstart = ''; + $linkend = ''; + + $result .= $linkstart; + if ($withpicto) $result .= img_object(($notooltip ? '' : $label), $picto, ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip ? 0 : 1); + if ($withpicto != 2) $result .= $this->ref; + $result .= $linkend; + if ($withpicto != 2) $result .= (($addlabel && $this->title) ? $sep.dol_trunc($this->title, ($addlabel > 1 ? $addlabel : 0)) : ''); + + global $action; + $hookmanager->initHooks(array('projectdao')); + $parameters = array('id'=>$this->id, 'getnomurl'=>$result); + $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks + if ($reshook > 0) $result = $hookmanager->resPrint; + else $result .= $hookmanager->resPrint; + + 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() + { + global $user, $langs, $conf; + + $now = dol_now(); + + // Initialise parameters + $this->id = 0; + $this->ref = 'SPECIMEN'; + $this->specimen = 1; + $this->socid = 1; + $this->date_c = $now; + $this->date_m = $now; + $this->date_start = $now; + $this->date_end = $now + (3600 * 24 * 365); + $this->note_public = 'SPECIMEN'; + $this->fk_ele = 20000; + $this->opp_amount = 20000; + $this->budget_amount = 10000; + + $this->usage_opportunity = 1; + $this->usage_task = 1; + $this->usage_bill_time = 1; + $this->usage_organize_event = 1; + + /* + $nbp = mt_rand(1, 9); + $xnbp = 0; + while ($xnbp < $nbp) + { + $line = new Task($this->db); + $line->fk_project = 0; + $line->label = $langs->trans("Label") . " " . $xnbp; + $line->description = $langs->trans("Description") . " " . $xnbp; + + $this->lines[]=$line; + $xnbp++; + } + */ + } + + /** + * Check if user has permission on current project + * + * @param User $user Object user to evaluate + * @param string $mode Type of permission we want to know: 'read', 'write' + * @return int >0 if user has permission, <0 if user has no permission + */ + public function restrictedProjectArea($user, $mode = 'read') + { + // To verify role of users + $userAccess = 0; + if (($mode == 'read' && !empty($user->rights->projet->all->lire)) || ($mode == 'write' && !empty($user->rights->projet->all->creer)) || ($mode == 'delete' && !empty($user->rights->projet->all->supprimer))) + { + $userAccess = 1; + } + elseif ($this->public && (($mode == 'read' && !empty($user->rights->projet->lire)) || ($mode == 'write' && !empty($user->rights->projet->creer)) || ($mode == 'delete' && !empty($user->rights->projet->supprimer)))) + { + $userAccess = 1; + } + else + { + foreach (array('internal', 'external') as $source) + { + $userRole = $this->liste_contact(4, $source); + $num = count($userRole); + + $nblinks = 0; + while ($nblinks < $num) + { + if ($source == 'internal' && preg_match('/^PROJECT/', $userRole[$nblinks]['code']) && $user->id == $userRole[$nblinks]['id']) + { + if ($mode == 'read' && $user->rights->projet->lire) $userAccess++; + if ($mode == 'write' && $user->rights->projet->creer) $userAccess++; + if ($mode == 'delete' && $user->rights->projet->supprimer) $userAccess++; + } + $nblinks++; + } + } + //if (empty($nblinks)) // If nobody has permission, we grant creator + //{ + // if ((!empty($this->user_author_id) && $this->user_author_id == $user->id)) + // { + // $userAccess = 1; + // } + //} + } + + return ($userAccess ? $userAccess : -1); + } + + /** + * Return array of projects a user has permission on, is affected to, or all projects + * + * @param User $user User object + * @param int $mode 0=All project I have permission on (assigned to me or public), 1=Projects assigned to me only, 2=Will return list of all projects with no test on contacts + * @param int $list 0=Return array, 1=Return string list + * @param int $socid 0=No filter on third party, id of third party + * @param string $filter additionnal filter on project (statut, ref, ...) + * @return array or string Array of projects id, or string with projects id separated with "," if list is 1 + */ + public function getProjectsAuthorizedForUser($user, $mode = 0, $list = 0, $socid = 0, $filter = '') + { + $projects = array(); + $temp = array(); + + $sql = "SELECT ".(($mode == 0 || $mode == 1) ? "DISTINCT " : "")."p.rowid, p.ref"; + $sql.= " FROM " . MAIN_DB_PREFIX . "projet as p"; + if ($mode == 0) + { + $sql.= " LEFT JOIN " . MAIN_DB_PREFIX . "element_contact as ec ON ec.element_id = p.rowid"; + } + elseif ($mode == 1) + { + $sql.= ", " . MAIN_DB_PREFIX . "element_contact as ec"; + } + elseif ($mode == 2) + { + // No filter. Use this if user has permission to see all project + } + $sql.= " WHERE p.entity IN (".getEntity('project').")"; + // Internal users must see project he is contact to even if project linked to a third party he can't see. + //if ($socid || ! $user->rights->societe->client->voir) $sql.= " AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = ".$socid.")"; + if ($socid > 0) $sql.= " AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = " . $socid . ")"; + + // Get id of types of contacts for projects (This list never contains a lot of elements) + $listofprojectcontacttype=array(); + $sql2 = "SELECT ctc.rowid, ctc.code FROM ".MAIN_DB_PREFIX."c_type_contact as ctc"; + $sql2.= " WHERE ctc.element = '" . $this->db->escape($this->element) . "'"; + $sql2.= " AND ctc.source = 'internal'"; + $resql = $this->db->query($sql2); + if ($resql) + { + while($obj = $this->db->fetch_object($resql)) + { + $listofprojectcontacttype[$obj->rowid]=$obj->code; + } + } + else dol_print_error($this->db); + if (count($listofprojectcontacttype) == 0) $listofprojectcontacttype[0]='0'; // To avoid syntax error if not found + + if ($mode == 0) + { + $sql.= " AND ( p.public = 1"; + $sql.= " OR ( ec.fk_c_type_contact IN (".join(',', array_keys($listofprojectcontacttype)).")"; + $sql.= " AND ec.fk_socpeople = ".$user->id.")"; + $sql.= " )"; + } + elseif ($mode == 1) + { + $sql.= " AND ec.element_id = p.rowid"; + $sql.= " AND ("; + $sql.= " ( ec.fk_c_type_contact IN (".join(',', array_keys($listofprojectcontacttype)).")"; + $sql.= " AND ec.fk_socpeople = ".$user->id.")"; + $sql.= " )"; + } + elseif ($mode == 2) + { + // No filter. Use this if user has permission to see all project + } + + $sql.= $filter; + //print $sql; + + $resql = $this->db->query($sql); + if ($resql) + { + $num = $this->db->num_rows($resql); + $i = 0; + while ($i < $num) + { + $row = $this->db->fetch_row($resql); + $projects[$row[0]] = $row[1]; + $temp[] = $row[0]; + $i++; + } + + $this->db->free($resql); + + if ($list) + { + if (empty($temp)) return '0'; + $result = implode(',', $temp); + return $result; + } + } + else + { + dol_print_error($this->db); + } + + return $projects; + } + + /** + * 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 bool $clone_contact Clone contact of project + * @param bool $clone_task Clone task of project + * @param bool $clone_project_file Clone file of project + * @param bool $clone_task_file Clone file of task (if task are copied) + * @param bool $clone_note Clone note of project + * @param bool $move_date Move task date on clone + * @param integer $notrigger No trigger flag + * @param int $newthirdpartyid New thirdparty id + * @return int New id of clone + */ + public function createFromClone(User $user, $fromid, $clone_contact = false, $clone_task = true, $clone_project_file = false, $clone_task_file = false, $clone_note = true, $move_date = true, $notrigger = 0, $newthirdpartyid = 0) + { + global $langs, $conf; + + $error = 0; + + dol_syslog("createFromClone clone_contact=".$clone_contact." clone_task=".$clone_task." clone_project_file=".$clone_project_file." clone_note=".$clone_note." move_date=".$move_date, LOG_DEBUG); + + $now = dol_mktime(0, 0, 0, idate('m', dol_now()), idate('d', dol_now()), idate('Y', dol_now())); + + $clone_project = new Project($this->db); + + $clone_project->context['createfromclone'] = 'createfromclone'; + + $this->db->begin(); + + // Load source object + $clone_project->fetch($fromid); + $clone_project->fetch_optionals(); + if ($newthirdpartyid > 0) $clone_project->socid = $newthirdpartyid; + $clone_project->fetch_thirdparty(); + + $orign_dt_start = $clone_project->date_start; + $orign_project_ref = $clone_project->ref; + + $clone_project->id = 0; + if ($move_date) { + $clone_project->date_start = $now; + if (!(empty($clone_project->date_end))) + { + $clone_project->date_end = $clone_project->date_end + ($now - $orign_dt_start); + } + } + + $clone_project->datec = $now; + + if (!$clone_note) + { + $clone_project->note_private = ''; + $clone_project->note_public = ''; + } + + //Generate next ref + $defaultref = ''; + $obj = empty($conf->global->PROJECT_ADDON) ? 'mod_project_simple' : $conf->global->PROJECT_ADDON; + // Search template files + $file = ''; $classname = ''; $filefound = 0; + $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']); + foreach ($dirmodels as $reldir) + { + $file = dol_buildpath($reldir."core/modules/project/".$obj.'.php', 0); + if (file_exists($file)) + { + $filefound = 1; + dol_include_once($reldir."core/modules/project/".$obj.'.php'); + $modProject = new $obj; + $defaultref = $modProject->getNextValue(is_object($clone_project->thirdparty) ? $clone_project->thirdparty : null, $clone_project); + break; + } + } + if (is_numeric($defaultref) && $defaultref <= 0) $defaultref = ''; + + $clone_project->ref = $defaultref; + $clone_project->title = $langs->trans("CopyOf").' '.$clone_project->title; + + // Create clone + $result = $clone_project->create($user, $notrigger); + + // Other options + if ($result < 0) + { + $this->error .= $clone_project->error; + $error++; + } + + if (!$error) + { + //Get the new project id + $clone_project_id = $clone_project->id; + + //Note Update + if (!$clone_note) + { + $clone_project->note_private = ''; + $clone_project->note_public = ''; + } + else + { + $this->db->begin(); + $res = $clone_project->update_note(dol_html_entity_decode($clone_project->note_public, ENT_QUOTES), '_public'); + if ($res < 0) + { + $this->error .= $clone_project->error; + $error++; + $this->db->rollback(); + } + else + { + $this->db->commit(); + } + + $this->db->begin(); + $res = $clone_project->update_note(dol_html_entity_decode($clone_project->note_private, ENT_QUOTES), '_private'); + if ($res < 0) + { + $this->error .= $clone_project->error; + $error++; + $this->db->rollback(); + } + else + { + $this->db->commit(); + } + } + + //Duplicate contact + if ($clone_contact) + { + $origin_project = new Project($this->db); + $origin_project->fetch($fromid); + + foreach (array('internal', 'external') as $source) + { + $tab = $origin_project->liste_contact(-1, $source); + + foreach ($tab as $contacttoadd) + { + $clone_project->add_contact($contacttoadd['id'], $contacttoadd['code'], $contacttoadd['source'], $notrigger); + if ($clone_project->error == 'DB_ERROR_RECORD_ALREADY_EXISTS') + { + $langs->load("errors"); + $this->error .= $langs->trans("ErrorThisContactIsAlreadyDefinedAsThisType"); + $error++; + } + else + { + if ($clone_project->error != '') + { + $this->error .= $clone_project->error; + $error++; + } + } + } + } + } + + //Duplicate file + if ($clone_project_file) + { + require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; + + $clone_project_dir = $conf->projet->dir_output."/".dol_sanitizeFileName($defaultref); + $ori_project_dir = $conf->projet->dir_output."/".dol_sanitizeFileName($orign_project_ref); + + if (dol_mkdir($clone_project_dir) >= 0) + { + $filearray = dol_dir_list($ori_project_dir, "files", 0, '', '(\.meta|_preview.*\.png)$', '', SORT_ASC, 1); + foreach ($filearray as $key => $file) + { + $rescopy = dol_copy($ori_project_dir.'/'.$file['name'], $clone_project_dir.'/'.$file['name'], 0, 1); + if (is_numeric($rescopy) && $rescopy < 0) + { + $this->error .= $langs->trans("ErrorFailToCopyFile", $ori_project_dir.'/'.$file['name'], $clone_project_dir.'/'.$file['name']); + $error++; + } + } + } + else + { + $this->error .= $langs->trans('ErrorInternalErrorDetected').':dol_mkdir'; + $error++; + } + } + + //Duplicate task + if ($clone_task) + { + require_once DOL_DOCUMENT_ROOT . '/projet/class/task.class.php'; + + $taskstatic = new Task($this->db); + + // Security check + $socid=0; + if ($user->socid > 0) $socid = $user->socid; + + $tasksarray=$taskstatic->getTasksArray(0, 0, $fromid, $socid, 0); + + $tab_conv_child_parent=array(); + + // Loop on each task, to clone it + foreach ($tasksarray as $tasktoclone) + { + $result_clone = $taskstatic->createFromClone($user, $tasktoclone->id, $clone_project_id, $tasktoclone->fk_parent, $move_date, true, false, $clone_task_file, true, false); + if ($result_clone <= 0) + { + $this->error .= $result_clone->error; + $error++; + } + else + { + $new_task_id = $result_clone; + $taskstatic->fetch($tasktoclone->id); + + //manage new parent clone task id + // if the current task has child we store the original task id and the equivalent clone task id + if (($taskstatic->hasChildren()) && !array_key_exists($tasktoclone->id, $tab_conv_child_parent)) + { + $tab_conv_child_parent[$tasktoclone->id] = $new_task_id; + } + } + } + + //Parse all clone node to be sure to update new parent + $tasksarray = $taskstatic->getTasksArray(0, 0, $clone_project_id, $socid, 0); + foreach ($tasksarray as $task_cloned) + { + $taskstatic->fetch($task_cloned->id); + if ($taskstatic->fk_task_parent != 0) + { + $taskstatic->fk_task_parent = $tab_conv_child_parent[$taskstatic->fk_task_parent]; + } + $res = $taskstatic->update($user, $notrigger); + if ($result_clone <= 0) + { + $this->error .= $taskstatic->error; + $error++; + } + } + } + } + + unset($clone_project->context['createfromclone']); + + if (!$error) + { + $this->db->commit(); + return $clone_project_id; + } + else + { + $this->db->rollback(); + dol_syslog(get_class($this)."::createFromClone nbError: ".$error." error : ".$this->error, LOG_ERR); + return -1; + } + } + + + /** + * Shift project task date from current date to delta + * + * @param integer $old_project_dt_start Old project start date + * @return int 1 if OK or < 0 if KO + */ + public function shiftTaskDate($old_project_dt_start) + { + global $user, $langs, $conf; + + $error=0; + + $taskstatic = new Task($this->db); + + // Security check + $socid=0; + if ($user->socid > 0) $socid = $user->socid; + + $tasksarray=$taskstatic->getTasksArray(0, 0, $this->id, $socid, 0); + + foreach ($tasksarray as $tasktoshiftdate) + { + $to_update=false; + // Fetch only if update of date will be made + if ((!empty($tasktoshiftdate->date_start)) || (!empty($tasktoshiftdate->date_end))) + { + //dol_syslog(get_class($this)."::shiftTaskDate to_update", LOG_DEBUG); + $to_update=true; + $task = new Task($this->db); + $result = $task->fetch($tasktoshiftdate->id); + if (!$result) + { + $error++; + $this->error.=$task->error; + } + } + //print "$this->date_start + $tasktoshiftdate->date_start - $old_project_dt_start";exit; + + //Calcultate new task start date with difference between old proj start date and origin task start date + if (!empty($tasktoshiftdate->date_start)) + { + $task->date_start = $this->date_start + ($tasktoshiftdate->date_start - $old_project_dt_start); + } + + //Calcultate new task end date with difference between origin proj end date and origin task end date + if (!empty($tasktoshiftdate->date_end)) + { + $task->date_end = $this->date_start + ($tasktoshiftdate->date_end - $old_project_dt_start); + } + + if ($to_update) + { + $result = $task->update($user); + if (!$result) + { + $error++; + $this->error .= $task->error; + } + } + } + if ($error != 0) + { + return -1; + } + return $result; + } + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Associate element to a project + * + * @param string $tableName Table of the element to update + * @param int $elementSelectId Key-rowid of the line of the element to update + * @return int 1 if OK or < 0 if KO + */ + public function update_element($tableName, $elementSelectId) + { + // phpcs:enable + $sql = "UPDATE ".MAIN_DB_PREFIX.$tableName; + + if ($tableName == "actioncomm") + { + $sql .= " SET fk_project=".$this->id; + $sql .= " WHERE id=".$elementSelectId; + } + else + { + $sql .= " SET fk_projet=".$this->id; + $sql .= " WHERE rowid=".$elementSelectId; + } + + dol_syslog(get_class($this)."::update_element", LOG_DEBUG); + $resql = $this->db->query($sql); + if (!$resql) { + $this->error = $this->db->lasterror(); + return -1; + } else { + return 1; + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Associate element to a project + * + * @param string $tableName Table of the element to update + * @param int $elementSelectId Key-rowid of the line of the element to update + * @param string $projectfield The column name that stores the link with the project + * + * @return int 1 if OK or < 0 if KO + */ + public function remove_element($tableName, $elementSelectId, $projectfield = 'fk_projet') + { + // phpcs:enable + $sql = "UPDATE ".MAIN_DB_PREFIX.$tableName; + + if ($tableName == "actioncomm") + { + $sql .= " SET fk_project=NULL"; + $sql .= " WHERE id=".$elementSelectId; + } else + { + $sql .= " SET ".$projectfield."=NULL"; + $sql .= " WHERE rowid=".$elementSelectId; + } + + dol_syslog(get_class($this)."::remove_element", LOG_DEBUG); + $resql = $this->db->query($sql); + if (!$resql) { + $this->error = $this->db->lasterror(); + return -1; + } else { + return 1; + } + } + + /** + * Create an intervention document on disk using template defined into PROJECT_ADDON_PDF + * + * @param string $modele Force template to use ('' by default) + * @param Translate $outputlangs Objet lang to use for translation + * @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,$langs; + + $langs->load("projects"); + + if (! dol_strlen($modele)) { + $modele = 'baleine'; + + if ($this->modelpdf) { + $modele = $this->modelpdf; + } elseif (! empty($conf->global->PROJECT_ADDON_PDF)) { + $modele = $conf->global->PROJECT_ADDON_PDF; + } + } + + $modelpath = "core/modules/project/doc/"; + + return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref); + } + + + /** + * Load time spent into this->weekWorkLoad and this->weekWorkLoadPerTask for all day of a week of project. + * Note: array weekWorkLoad and weekWorkLoadPerTask are reset and filled at each call. + * + * @param int $datestart First day of week (use dol_get_first_day to find this date) + * @param int $taskid Filter on a task id + * @param int $userid Time spent by a particular user + * @return int <0 if OK, >0 if KO + */ + public function loadTimeSpent($datestart, $taskid = 0, $userid = 0) + { + $error=0; + + $this->weekWorkLoad=array(); + $this->weekWorkLoadPerTask=array(); + + if (empty($datestart)) dol_print_error('', 'Error datestart parameter is empty'); + + $sql = "SELECT ptt.rowid as taskid, ptt.task_duration, ptt.task_date, ptt.task_datehour, ptt.fk_task"; + $sql.= " FROM ".MAIN_DB_PREFIX."projet_task_time AS ptt, ".MAIN_DB_PREFIX."projet_task as pt"; + $sql.= " WHERE ptt.fk_task = pt.rowid"; + $sql.= " AND pt.fk_projet = ".$this->id; + $sql.= " AND (ptt.task_date >= '".$this->db->idate($datestart)."' "; + $sql.= " AND ptt.task_date <= '".$this->db->idate(dol_time_plus_duree($datestart, 1, 'w') - 1)."')"; + if ($taskid) $sql.= " AND ptt.fk_task=".$taskid; + if (is_numeric($userid)) $sql.= " AND ptt.fk_user=".$userid; + + //print $sql; + $resql=$this->db->query($sql); + if ($resql) + { + $daylareadyfound=array(); + + $num = $this->db->num_rows($resql); + $i = 0; + // Loop on each record found, so each couple (project id, task id) + while ($i < $num) + { + $obj=$this->db->fetch_object($resql); + $day=$this->db->jdate($obj->task_date); // task_date is date without hours + if (empty($daylareadyfound[$day])) + { + $this->weekWorkLoad[$day] = $obj->task_duration; + $this->weekWorkLoadPerTask[$day][$obj->fk_task] = $obj->task_duration; + } + else + { + $this->weekWorkLoad[$day] += $obj->task_duration; + $this->weekWorkLoadPerTask[$day][$obj->fk_task] += $obj->task_duration; + } + $daylareadyfound[$day]=1; + $i++; + } + $this->db->free($resql); + return 1; + } + else + { + $this->error = "Error ".$this->db->lasterror(); + dol_syslog(get_class($this)."::fetch ".$this->error, LOG_ERR); + return -1; + } + } + + + // 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; + + $projectsListId = null; + if (!$user->rights->projet->all->lire) $projectsListId = $this->getProjectsAuthorizedForUser($user, 0, 1); + + $sql = "SELECT p.rowid, p.fk_statut as status, p.fk_opp_status, p.datee as datee"; + $sql .= " FROM (".MAIN_DB_PREFIX."projet as p"; + $sql .= ")"; + $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s on p.fk_soc = s.rowid"; + // For external user, no check is done on company permission because readability is managed by public status of project and assignement. + //if (! $user->rights->societe->client->voir && ! $socid) $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON sc.fk_soc = s.rowid"; + $sql .= " WHERE p.fk_statut = 1"; + $sql .= " AND p.entity IN (".getEntity('project').')'; + if (!empty($projectsListId)) $sql .= " AND p.rowid IN (".$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 = ".$socid.")"; + // For external user, no check is done on company permission because readability is managed by public status of project and assignement. + //if (! $user->rights->societe->client->voir && ! $socid) $sql.= " AND ((s.rowid = sc.fk_soc AND sc.fk_user = " .$user->id.") OR (s.rowid IS NULL))"; + + //print $sql; + $resql = $this->db->query($sql); + if ($resql) + { + $project_static = new Project($this->db); + + $response = new WorkboardResponse(); + $response->warning_delay = $conf->projet->warning_delay / 60 / 60 / 24; + $response->label = $langs->trans("OpenedProjects"); + $response->labelShort = $langs->trans("Opened"); + if ($user->rights->projet->all->lire) $response->url = DOL_URL_ROOT.'/projet/list.php?search_status=1&mainmenu=project'; + else $response->url = DOL_URL_ROOT.'/projet/list.php?search_project_user=-1&search_status=1&mainmenu=project'; + $response->img = img_object('', "projectpub"); + + // This assignment in condition is not a bug. It allows walking the results. + while ($obj = $this->db->fetch_object($resql)) + { + $response->nbtodo++; + + $project_static->statut = $obj->status; + $project_static->opp_status = $obj->opp_status; + $project_static->datee = $this->db->jdate($obj->datee); + + if ($project_static->hasDelay()) { + $response->nbtodolate++; + } + } + + return $response; + } + else + { + $this->error = $this->db->error(); + return -1; + } + } + + + /** + * Function used to replace a thirdparty id with another one. + * + * @param DoliDB $db Database handler + * @param int $origin_id Old thirdparty id + * @param int $dest_id New thirdparty id + * @return bool + */ + public static function replaceThirdparty(DoliDB $db, $origin_id, $dest_id) + { + $tables = array( + 'projet' + ); + + return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables); + } + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Charge indicateurs this->nb pour le tableau de bord + * + * @return int <0 if KO, >0 if OK + */ + public function load_state_board() + { + // phpcs:enable + global $user; + + $this->nb = array(); + + $sql = "SELECT count(p.rowid) as nb"; + $sql .= " FROM ".MAIN_DB_PREFIX."projet as p"; + $sql .= " WHERE"; + $sql .= " p.entity IN (".getEntity('project').")"; + if (!$user->rights->projet->all->lire) + { + $projectsListId = $this->getProjectsAuthorizedForUser($user, 0, 1); + $sql .= "AND p.rowid IN (".$projectsListId.")"; + } + + $resql = $this->db->query($sql); + if ($resql) + { + while ($obj = $this->db->fetch_object($resql)) + { + $this->nb["projects"] = $obj->nb; + } + $this->db->free($resql); + return 1; + } + else + { + dol_print_error($this->db); + $this->error = $this->db->error(); + return -1; + } + } + + + /** + * Is the project delayed? + * + * @return bool + */ + public function hasDelay() + { + global $conf; + + if (!($this->statut == self::STATUS_VALIDATED)) return false; + if (!$this->datee && !$this->date_end) return false; + + $now = dol_now(); + + return ($this->datee ? $this->datee : $this->date_end) < ($now - $conf->projet->warning_delay); + } + + + /** + * Charge les informations d'ordre info dans l'objet commande + * + * @param int $id Id of order + * @return void + */ + public function info($id) + { + $sql = 'SELECT c.rowid, datec as datec, tms as datem,'; + $sql .= ' date_close as datecloture,'; + $sql .= ' fk_user_creat as fk_user_author, fk_user_close as fk_use_cloture'; + $sql .= ' FROM '.MAIN_DB_PREFIX.'projet as c'; + $sql .= ' WHERE c.rowid = '.$id; + $result = $this->db->query($sql); + if ($result) + { + if ($this->db->num_rows($result)) + { + $obj = $this->db->fetch_object($result); + $this->id = $obj->rowid; + if ($obj->fk_user_author) + { + $cuser = new User($this->db); + $cuser->fetch($obj->fk_user_author); + $this->user_creation = $cuser; + } + + if ($obj->fk_user_cloture) + { + $cluser = new User($this->db); + $cluser->fetch($obj->fk_user_cloture); + $this->user_cloture = $cluser; + } + + $this->date_creation = $this->db->jdate($obj->datec); + $this->date_modification = $this->db->jdate($obj->datem); + $this->date_cloture = $this->db->jdate($obj->datecloture); + } + + $this->db->free($result); + } + else + { + dol_print_error($this->db); + } + } + + /** + * Sets object to supplied categories. + * + * Deletes object from existing categories not supplied. + * Adds it to non existing supplied categories. + * Existing categories are left untouch. + * + * @param int[]|int $categories Category or categories IDs + * @return void + */ + public function setCategories($categories) + { + $type_categ = Categorie::TYPE_PROJECT; + + // Handle single category + if (!is_array($categories)) { + $categories = array($categories); + } + + // Get current categories + require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php'; + $c = new Categorie($this->db); + $existing = $c->containing($this->id, $type_categ, 'id'); + + // Diff + if (is_array($existing)) { + $to_del = array_diff($existing, $categories); + $to_add = array_diff($categories, $existing); + } else { + $to_del = array(); // Nothing to delete + $to_add = $categories; + } + + // Process + foreach ($to_del as $del) { + if ($c->fetch($del) > 0) { + $result = $c->del_type($this, $type_categ); + if ($result < 0) { + $this->errors = $c->errors; + $this->error = $c->error; + return -1; + } + } + } + foreach ($to_add as $add) { + if ($c->fetch($add) > 0) { + $result = $c->add_type($this, $type_categ); + if ($result < 0) { + $this->errors = $c->errors; + $this->error = $c->error; + return -1; + } + } + } + + return 1; + } + + + /** + * Create an array of tasks of current project + * + * @param User $user Object user we want project allowed to + * @return int >0 if OK, <0 if KO + */ + public function getLinesArray($user) + { + require_once DOL_DOCUMENT_ROOT.'/projet/class/task.class.php'; + $taskstatic = new Task($this->db); + + $this->lines = $taskstatic->getTasksArray(0, $user, $this->id, 0, 0); + } } From 434341316bbee837d24e168e7f261f2cff3ac451 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 20 May 2020 19:51:04 +0200 Subject: [PATCH 2/4] suggest some fix to debug situation invoices --- htdocs/core/tpl/objectline_title.tpl.php | 2 +- htdocs/install/mysql/migration/repair.sql | 5 +++++ htdocs/langs/en_US/bills.lang | 3 ++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/htdocs/core/tpl/objectline_title.tpl.php b/htdocs/core/tpl/objectline_title.tpl.php index fd848764721..c2f2dbb3d42 100644 --- a/htdocs/core/tpl/objectline_title.tpl.php +++ b/htdocs/core/tpl/objectline_title.tpl.php @@ -83,7 +83,7 @@ print ''.$langs->trans('ReductionShort').'situation_cycle_ref) { print '' . $langs->trans('Progress') . ''; - print '' . $langs->trans('TotalHT100Short') . ''; + print '' . $form->textwithpicto($langs->trans('TotalHT100Short'), $langs->trans('UnitPriceXQtyLessDiscount')) . ''; } if ($usemargins && ! empty($conf->margin->enabled) && empty($user->societe_id)) diff --git a/htdocs/install/mysql/migration/repair.sql b/htdocs/install/mysql/migration/repair.sql index 64e81e30329..fe0eaff767a 100755 --- a/htdocs/install/mysql/migration/repair.sql +++ b/htdocs/install/mysql/migration/repair.sql @@ -475,6 +475,11 @@ UPDATE llx_accounting_bookkeeping set date_creation = tms where date_creation IS -- UPDATE llx_facturedet_rec set label = NULL WHERE label IS NOT NULL; +-- Test inconsistency of data into situation invoices: If it differs, it may be the total_ht that is wrong and situation_percent that is good. +-- select f.rowid, f.type, qty, subprice, situation_percent, total_ht, total_ttc, total_tva, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc, (situation_percent / 100 * subprice * qty * (1 - (fd.remise_percent / 100))) +-- from llx_facturedet as fd, llx_facture as f where fd.fk_facture = f.rowid AND (total_ht - situation_percent / 100 * subprice * qty * (1 - (fd.remise_percent / 100))) > 0.01 and f.type = 5; + + -- Note to migrate from old counter aquarium to new one -- drop table tmp; diff --git a/htdocs/langs/en_US/bills.lang b/htdocs/langs/en_US/bills.lang index ddef01b17c1..3f6dc358d2f 100644 --- a/htdocs/langs/en_US/bills.lang +++ b/htdocs/langs/en_US/bills.lang @@ -554,4 +554,5 @@ AutoFillDateFromShort=Set start date AutoFillDateTo=Set end date for service line with next invoice date AutoFillDateToShort=Set end date MaxNumberOfGenerationReached=Max number of gen. reached -BILL_DELETEInDolibarr=Invoice deleted \ No newline at end of file +BILL_DELETEInDolibarr=Invoice deleted +UnitPriceXQtyLessDiscount=Unit price x Qty - Discount \ No newline at end of file From 650bddde0d6942fcaa7fe2d383a2ebb7b2d485e1 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 20 May 2020 23:25:10 +0200 Subject: [PATCH 3/4] Fix phpcs --- htdocs/projet/class/project.class.php | 125 +++++++++++++------------- 1 file changed, 64 insertions(+), 61 deletions(-) diff --git a/htdocs/projet/class/project.class.php b/htdocs/projet/class/project.class.php index 78f5bac2fe7..1e6c675d6fe 100644 --- a/htdocs/projet/class/project.class.php +++ b/htdocs/projet/class/project.class.php @@ -1047,76 +1047,79 @@ class Project extends CommonObject if ($option != 'nolink') $label = ''.$langs->trans("ShowProject").''; $label .= ($label ? '
' : '').''.$langs->trans('Ref').': '.$this->ref; // The space must be after the : to not being explode when showing the title in img_picto $label .= ($label ? '
' : '').''.$langs->trans('Label').': '.$this->title; // The space must be after the : to not being explode when showing the title in img_picto - if (!empty($this->thirdparty_name)) + if (!empty($this->thirdparty_name)) { $label .= ($label ? '
' : '').''.$langs->trans('ThirdParty').': '.$this->thirdparty_name; // The space must be after the : to not being explode when showing the title in img_picto - if (!empty($this->dateo)) - $label .= ($label ? '
' : '').''.$langs->trans('DateStart').': '.dol_print_date($this->dateo, 'day'); // The space must be after the : to not being explode when showing the title in img_picto - if (!empty($this->datee)) - $label .= ($label ? '
' : '').''.$langs->trans('DateEnd').': '.dol_print_date($this->datee, 'day'); // The space must be after the : to not being explode when showing the title in img_picto - if ($moreinpopup) $label .= '
'.$moreinpopup; + } + if (!empty($this->dateo)) { + $label .= ($label ? '
' : '').''.$langs->trans('DateStart').': '.dol_print_date($this->dateo, 'day'); // The space must be after the : to not being explode when showing the title in img_picto + } + if (!empty($this->datee)) { + $label .= ($label ? '
' : '').''.$langs->trans('DateEnd').': '.dol_print_date($this->datee, 'day'); // The space must be after the : to not being explode when showing the title in img_picto + } + if ($moreinpopup) $label .= '
'.$moreinpopup; - $url = ''; - if ($option != 'nolink') - { - if (preg_match('/\.php$/', $option)) { - $url = dol_buildpath($option, 1).'?id='.$this->id; - } - elseif ($option == 'task') - { - $url = DOL_URL_ROOT.'/projet/tasks.php?id='.$this->id; - } - else - { - $url = DOL_URL_ROOT.'/projet/card.php?id='.$this->id; - } - // Add param to save lastsearch_values or not - $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0); - if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) $add_save_lastsearch_values = 1; - if ($add_save_lastsearch_values) $url .= '&save_lastsearch_values=1'; - } + $url = ''; + if ($option != 'nolink') + { + if (preg_match('/\.php$/', $option)) { + $url = dol_buildpath($option, 1).'?id='.$this->id; + } + elseif ($option == 'task') + { + $url = DOL_URL_ROOT.'/projet/tasks.php?id='.$this->id; + } + else + { + $url = DOL_URL_ROOT.'/projet/card.php?id='.$this->id; + } + // Add param to save lastsearch_values or not + $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0); + if ($save_lastsearch_value == -1 && 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) && $user->rights->projet->lire) - { - if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) - { - $label = $langs->trans("ShowProject"); - $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"'; - } - $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"'; - $linkclose .= ' class="classfortooltip"'; + $linkclose = ''; + if (empty($notooltip) && $user->rights->projet->lire) + { + if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) + { + $label = $langs->trans("ShowProject"); + $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"'; + } + $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"'; + $linkclose .= ' class="classfortooltip"'; - /* - $hookmanager->initHooks(array('projectdao')); - $parameters=array('id'=>$this->id); - // Note that $action and $object may have been modified by some hooks - $reshook=$hookmanager->executeHooks('getnomurltooltip',$parameters,$this,$action); - if ($reshook > 0) - $linkclose = $hookmanager->resPrint; - */ - } + /* + $hookmanager->initHooks(array('projectdao')); + $parameters=array('id'=>$this->id); + // Note that $action and $object may have been modified by some hooks + $reshook=$hookmanager->executeHooks('getnomurltooltip',$parameters,$this,$action); + if ($reshook > 0) + $linkclose = $hookmanager->resPrint; + */ + } - $picto = 'projectpub'; - if (!$this->public) $picto = 'project'; + $picto = 'projectpub'; + if (!$this->public) $picto = 'project'; - $linkstart = ''; - $linkend = ''; + $linkstart = ''; + $linkend = ''; - $result .= $linkstart; - if ($withpicto) $result .= img_object(($notooltip ? '' : $label), $picto, ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip ? 0 : 1); - if ($withpicto != 2) $result .= $this->ref; - $result .= $linkend; - if ($withpicto != 2) $result .= (($addlabel && $this->title) ? $sep.dol_trunc($this->title, ($addlabel > 1 ? $addlabel : 0)) : ''); + $result .= $linkstart; + if ($withpicto) $result .= img_object(($notooltip ? '' : $label), $picto, ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip ? 0 : 1); + if ($withpicto != 2) $result .= $this->ref; + $result .= $linkend; + if ($withpicto != 2) $result .= (($addlabel && $this->title) ? $sep.dol_trunc($this->title, ($addlabel > 1 ? $addlabel : 0)) : ''); - global $action; - $hookmanager->initHooks(array('projectdao')); - $parameters = array('id'=>$this->id, 'getnomurl'=>$result); - $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks - if ($reshook > 0) $result = $hookmanager->resPrint; - else $result .= $hookmanager->resPrint; + global $action; + $hookmanager->initHooks(array('projectdao')); + $parameters = array('id'=>$this->id, 'getnomurl'=>$result); + $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks + if ($reshook > 0) $result = $hookmanager->resPrint; + else $result .= $hookmanager->resPrint; - return $result; + return $result; } /** From 9e3e8df60fa6260ae2581b8212a55c163ae7e6cb Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 20 May 2020 23:56:06 +0200 Subject: [PATCH 4/4] Fix phpcs --- htdocs/projet/class/project.class.php | 1576 ++++++++++++------------- 1 file changed, 788 insertions(+), 788 deletions(-) diff --git a/htdocs/projet/class/project.class.php b/htdocs/projet/class/project.class.php index 1e6c675d6fe..a5e1c197f8b 100644 --- a/htdocs/projet/class/project.class.php +++ b/htdocs/projet/class/project.class.php @@ -1215,875 +1215,875 @@ class Project extends CommonObject // $userAccess = 1; // } //} - } - - return ($userAccess ? $userAccess : -1); } - /** - * Return array of projects a user has permission on, is affected to, or all projects - * - * @param User $user User object - * @param int $mode 0=All project I have permission on (assigned to me or public), 1=Projects assigned to me only, 2=Will return list of all projects with no test on contacts - * @param int $list 0=Return array, 1=Return string list - * @param int $socid 0=No filter on third party, id of third party - * @param string $filter additionnal filter on project (statut, ref, ...) - * @return array or string Array of projects id, or string with projects id separated with "," if list is 1 - */ - public function getProjectsAuthorizedForUser($user, $mode = 0, $list = 0, $socid = 0, $filter = '') + return ($userAccess ? $userAccess : -1); + } + + /** + * Return array of projects a user has permission on, is affected to, or all projects + * + * @param User $user User object + * @param int $mode 0=All project I have permission on (assigned to me or public), 1=Projects assigned to me only, 2=Will return list of all projects with no test on contacts + * @param int $list 0=Return array, 1=Return string list + * @param int $socid 0=No filter on third party, id of third party + * @param string $filter additionnal filter on project (statut, ref, ...) + * @return array or string Array of projects id, or string with projects id separated with "," if list is 1 + */ + public function getProjectsAuthorizedForUser($user, $mode = 0, $list = 0, $socid = 0, $filter = '') + { + $projects = array(); + $temp = array(); + + $sql = "SELECT ".(($mode == 0 || $mode == 1) ? "DISTINCT " : "")."p.rowid, p.ref"; + $sql.= " FROM " . MAIN_DB_PREFIX . "projet as p"; + if ($mode == 0) { - $projects = array(); - $temp = array(); + $sql.= " LEFT JOIN " . MAIN_DB_PREFIX . "element_contact as ec ON ec.element_id = p.rowid"; + } + elseif ($mode == 1) + { + $sql.= ", " . MAIN_DB_PREFIX . "element_contact as ec"; + } + elseif ($mode == 2) + { + // No filter. Use this if user has permission to see all project + } + $sql.= " WHERE p.entity IN (".getEntity('project').")"; + // Internal users must see project he is contact to even if project linked to a third party he can't see. + //if ($socid || ! $user->rights->societe->client->voir) $sql.= " AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = ".$socid.")"; + if ($socid > 0) $sql.= " AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = " . $socid . ")"; - $sql = "SELECT ".(($mode == 0 || $mode == 1) ? "DISTINCT " : "")."p.rowid, p.ref"; - $sql.= " FROM " . MAIN_DB_PREFIX . "projet as p"; - if ($mode == 0) + // Get id of types of contacts for projects (This list never contains a lot of elements) + $listofprojectcontacttype=array(); + $sql2 = "SELECT ctc.rowid, ctc.code FROM ".MAIN_DB_PREFIX."c_type_contact as ctc"; + $sql2.= " WHERE ctc.element = '" . $this->db->escape($this->element) . "'"; + $sql2.= " AND ctc.source = 'internal'"; + $resql = $this->db->query($sql2); + if ($resql) + { + while($obj = $this->db->fetch_object($resql)) { - $sql.= " LEFT JOIN " . MAIN_DB_PREFIX . "element_contact as ec ON ec.element_id = p.rowid"; + $listofprojectcontacttype[$obj->rowid]=$obj->code; } - elseif ($mode == 1) - { - $sql.= ", " . MAIN_DB_PREFIX . "element_contact as ec"; - } - elseif ($mode == 2) - { - // No filter. Use this if user has permission to see all project - } - $sql.= " WHERE p.entity IN (".getEntity('project').")"; - // Internal users must see project he is contact to even if project linked to a third party he can't see. - //if ($socid || ! $user->rights->societe->client->voir) $sql.= " AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = ".$socid.")"; - if ($socid > 0) $sql.= " AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = " . $socid . ")"; + } + else dol_print_error($this->db); + if (count($listofprojectcontacttype) == 0) $listofprojectcontacttype[0]='0'; // To avoid syntax error if not found - // Get id of types of contacts for projects (This list never contains a lot of elements) - $listofprojectcontacttype=array(); - $sql2 = "SELECT ctc.rowid, ctc.code FROM ".MAIN_DB_PREFIX."c_type_contact as ctc"; - $sql2.= " WHERE ctc.element = '" . $this->db->escape($this->element) . "'"; - $sql2.= " AND ctc.source = 'internal'"; - $resql = $this->db->query($sql2); - if ($resql) - { - while($obj = $this->db->fetch_object($resql)) - { - $listofprojectcontacttype[$obj->rowid]=$obj->code; - } - } - else dol_print_error($this->db); - if (count($listofprojectcontacttype) == 0) $listofprojectcontacttype[0]='0'; // To avoid syntax error if not found - - if ($mode == 0) - { - $sql.= " AND ( p.public = 1"; - $sql.= " OR ( ec.fk_c_type_contact IN (".join(',', array_keys($listofprojectcontacttype)).")"; - $sql.= " AND ec.fk_socpeople = ".$user->id.")"; - $sql.= " )"; - } - elseif ($mode == 1) - { - $sql.= " AND ec.element_id = p.rowid"; - $sql.= " AND ("; - $sql.= " ( ec.fk_c_type_contact IN (".join(',', array_keys($listofprojectcontacttype)).")"; - $sql.= " AND ec.fk_socpeople = ".$user->id.")"; - $sql.= " )"; - } - elseif ($mode == 2) - { - // No filter. Use this if user has permission to see all project - } - - $sql.= $filter; - //print $sql; - - $resql = $this->db->query($sql); - if ($resql) - { - $num = $this->db->num_rows($resql); - $i = 0; - while ($i < $num) - { - $row = $this->db->fetch_row($resql); - $projects[$row[0]] = $row[1]; - $temp[] = $row[0]; - $i++; - } - - $this->db->free($resql); - - if ($list) - { - if (empty($temp)) return '0'; - $result = implode(',', $temp); - return $result; - } - } - else - { - dol_print_error($this->db); - } - - return $projects; + if ($mode == 0) + { + $sql.= " AND ( p.public = 1"; + $sql.= " OR ( ec.fk_c_type_contact IN (".join(',', array_keys($listofprojectcontacttype)).")"; + $sql.= " AND ec.fk_socpeople = ".$user->id.")"; + $sql.= " )"; + } + elseif ($mode == 1) + { + $sql.= " AND ec.element_id = p.rowid"; + $sql.= " AND ("; + $sql.= " ( ec.fk_c_type_contact IN (".join(',', array_keys($listofprojectcontacttype)).")"; + $sql.= " AND ec.fk_socpeople = ".$user->id.")"; + $sql.= " )"; + } + elseif ($mode == 2) + { + // No filter. Use this if user has permission to see all project } - /** - * 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 bool $clone_contact Clone contact of project - * @param bool $clone_task Clone task of project - * @param bool $clone_project_file Clone file of project - * @param bool $clone_task_file Clone file of task (if task are copied) - * @param bool $clone_note Clone note of project - * @param bool $move_date Move task date on clone - * @param integer $notrigger No trigger flag - * @param int $newthirdpartyid New thirdparty id - * @return int New id of clone - */ - public function createFromClone(User $user, $fromid, $clone_contact = false, $clone_task = true, $clone_project_file = false, $clone_task_file = false, $clone_note = true, $move_date = true, $notrigger = 0, $newthirdpartyid = 0) + $sql.= $filter; + //print $sql; + + $resql = $this->db->query($sql); + if ($resql) { - global $langs, $conf; - - $error = 0; - - dol_syslog("createFromClone clone_contact=".$clone_contact." clone_task=".$clone_task." clone_project_file=".$clone_project_file." clone_note=".$clone_note." move_date=".$move_date, LOG_DEBUG); - - $now = dol_mktime(0, 0, 0, idate('m', dol_now()), idate('d', dol_now()), idate('Y', dol_now())); - - $clone_project = new Project($this->db); - - $clone_project->context['createfromclone'] = 'createfromclone'; - - $this->db->begin(); - - // Load source object - $clone_project->fetch($fromid); - $clone_project->fetch_optionals(); - if ($newthirdpartyid > 0) $clone_project->socid = $newthirdpartyid; - $clone_project->fetch_thirdparty(); - - $orign_dt_start = $clone_project->date_start; - $orign_project_ref = $clone_project->ref; - - $clone_project->id = 0; - if ($move_date) { - $clone_project->date_start = $now; - if (!(empty($clone_project->date_end))) - { - $clone_project->date_end = $clone_project->date_end + ($now - $orign_dt_start); - } + $num = $this->db->num_rows($resql); + $i = 0; + while ($i < $num) + { + $row = $this->db->fetch_row($resql); + $projects[$row[0]] = $row[1]; + $temp[] = $row[0]; + $i++; } - $clone_project->datec = $now; + $this->db->free($resql); + if ($list) + { + if (empty($temp)) return '0'; + $result = implode(',', $temp); + return $result; + } + } + else + { + dol_print_error($this->db); + } + + return $projects; + } + + /** + * 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 bool $clone_contact Clone contact of project + * @param bool $clone_task Clone task of project + * @param bool $clone_project_file Clone file of project + * @param bool $clone_task_file Clone file of task (if task are copied) + * @param bool $clone_note Clone note of project + * @param bool $move_date Move task date on clone + * @param integer $notrigger No trigger flag + * @param int $newthirdpartyid New thirdparty id + * @return int New id of clone + */ + public function createFromClone(User $user, $fromid, $clone_contact = false, $clone_task = true, $clone_project_file = false, $clone_task_file = false, $clone_note = true, $move_date = true, $notrigger = 0, $newthirdpartyid = 0) + { + global $langs, $conf; + + $error = 0; + + dol_syslog("createFromClone clone_contact=".$clone_contact." clone_task=".$clone_task." clone_project_file=".$clone_project_file." clone_note=".$clone_note." move_date=".$move_date, LOG_DEBUG); + + $now = dol_mktime(0, 0, 0, idate('m', dol_now()), idate('d', dol_now()), idate('Y', dol_now())); + + $clone_project = new Project($this->db); + + $clone_project->context['createfromclone'] = 'createfromclone'; + + $this->db->begin(); + + // Load source object + $clone_project->fetch($fromid); + $clone_project->fetch_optionals(); + if ($newthirdpartyid > 0) $clone_project->socid = $newthirdpartyid; + $clone_project->fetch_thirdparty(); + + $orign_dt_start = $clone_project->date_start; + $orign_project_ref = $clone_project->ref; + + $clone_project->id = 0; + if ($move_date) { + $clone_project->date_start = $now; + if (!(empty($clone_project->date_end))) + { + $clone_project->date_end = $clone_project->date_end + ($now - $orign_dt_start); + } + } + + $clone_project->datec = $now; + + if (!$clone_note) + { + $clone_project->note_private = ''; + $clone_project->note_public = ''; + } + + //Generate next ref + $defaultref = ''; + $obj = empty($conf->global->PROJECT_ADDON) ? 'mod_project_simple' : $conf->global->PROJECT_ADDON; + // Search template files + $file = ''; $classname = ''; $filefound = 0; + $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']); + foreach ($dirmodels as $reldir) + { + $file = dol_buildpath($reldir."core/modules/project/".$obj.'.php', 0); + if (file_exists($file)) + { + $filefound = 1; + dol_include_once($reldir."core/modules/project/".$obj.'.php'); + $modProject = new $obj; + $defaultref = $modProject->getNextValue(is_object($clone_project->thirdparty) ? $clone_project->thirdparty : null, $clone_project); + break; + } + } + if (is_numeric($defaultref) && $defaultref <= 0) $defaultref = ''; + + $clone_project->ref = $defaultref; + $clone_project->title = $langs->trans("CopyOf").' '.$clone_project->title; + + // Create clone + $result = $clone_project->create($user, $notrigger); + + // Other options + if ($result < 0) + { + $this->error .= $clone_project->error; + $error++; + } + + if (!$error) + { + //Get the new project id + $clone_project_id = $clone_project->id; + + //Note Update if (!$clone_note) { $clone_project->note_private = ''; $clone_project->note_public = ''; } - - //Generate next ref - $defaultref = ''; - $obj = empty($conf->global->PROJECT_ADDON) ? 'mod_project_simple' : $conf->global->PROJECT_ADDON; - // Search template files - $file = ''; $classname = ''; $filefound = 0; - $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']); - foreach ($dirmodels as $reldir) + else { - $file = dol_buildpath($reldir."core/modules/project/".$obj.'.php', 0); - if (file_exists($file)) + $this->db->begin(); + $res = $clone_project->update_note(dol_html_entity_decode($clone_project->note_public, ENT_QUOTES), '_public'); + if ($res < 0) { - $filefound = 1; - dol_include_once($reldir."core/modules/project/".$obj.'.php'); - $modProject = new $obj; - $defaultref = $modProject->getNextValue(is_object($clone_project->thirdparty) ? $clone_project->thirdparty : null, $clone_project); - break; - } - } - if (is_numeric($defaultref) && $defaultref <= 0) $defaultref = ''; - - $clone_project->ref = $defaultref; - $clone_project->title = $langs->trans("CopyOf").' '.$clone_project->title; - - // Create clone - $result = $clone_project->create($user, $notrigger); - - // Other options - if ($result < 0) - { - $this->error .= $clone_project->error; - $error++; - } - - if (!$error) - { - //Get the new project id - $clone_project_id = $clone_project->id; - - //Note Update - if (!$clone_note) - { - $clone_project->note_private = ''; - $clone_project->note_public = ''; + $this->error .= $clone_project->error; + $error++; + $this->db->rollback(); } else { - $this->db->begin(); - $res = $clone_project->update_note(dol_html_entity_decode($clone_project->note_public, ENT_QUOTES), '_public'); - if ($res < 0) - { - $this->error .= $clone_project->error; - $error++; - $this->db->rollback(); - } - else - { - $this->db->commit(); - } - - $this->db->begin(); - $res = $clone_project->update_note(dol_html_entity_decode($clone_project->note_private, ENT_QUOTES), '_private'); - if ($res < 0) - { - $this->error .= $clone_project->error; - $error++; - $this->db->rollback(); - } - else - { - $this->db->commit(); - } + $this->db->commit(); } - //Duplicate contact - if ($clone_contact) + $this->db->begin(); + $res = $clone_project->update_note(dol_html_entity_decode($clone_project->note_private, ENT_QUOTES), '_private'); + if ($res < 0) { - $origin_project = new Project($this->db); - $origin_project->fetch($fromid); - - foreach (array('internal', 'external') as $source) - { - $tab = $origin_project->liste_contact(-1, $source); - - foreach ($tab as $contacttoadd) - { - $clone_project->add_contact($contacttoadd['id'], $contacttoadd['code'], $contacttoadd['source'], $notrigger); - if ($clone_project->error == 'DB_ERROR_RECORD_ALREADY_EXISTS') - { - $langs->load("errors"); - $this->error .= $langs->trans("ErrorThisContactIsAlreadyDefinedAsThisType"); - $error++; - } - else - { - if ($clone_project->error != '') - { - $this->error .= $clone_project->error; - $error++; - } - } - } - } + $this->error .= $clone_project->error; + $error++; + $this->db->rollback(); } - - //Duplicate file - if ($clone_project_file) + else { - require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; - - $clone_project_dir = $conf->projet->dir_output."/".dol_sanitizeFileName($defaultref); - $ori_project_dir = $conf->projet->dir_output."/".dol_sanitizeFileName($orign_project_ref); - - if (dol_mkdir($clone_project_dir) >= 0) - { - $filearray = dol_dir_list($ori_project_dir, "files", 0, '', '(\.meta|_preview.*\.png)$', '', SORT_ASC, 1); - foreach ($filearray as $key => $file) - { - $rescopy = dol_copy($ori_project_dir.'/'.$file['name'], $clone_project_dir.'/'.$file['name'], 0, 1); - if (is_numeric($rescopy) && $rescopy < 0) - { - $this->error .= $langs->trans("ErrorFailToCopyFile", $ori_project_dir.'/'.$file['name'], $clone_project_dir.'/'.$file['name']); - $error++; - } - } - } - else - { - $this->error .= $langs->trans('ErrorInternalErrorDetected').':dol_mkdir'; - $error++; - } + $this->db->commit(); } + } - //Duplicate task - if ($clone_task) + //Duplicate contact + if ($clone_contact) + { + $origin_project = new Project($this->db); + $origin_project->fetch($fromid); + + foreach (array('internal', 'external') as $source) { - require_once DOL_DOCUMENT_ROOT . '/projet/class/task.class.php'; + $tab = $origin_project->liste_contact(-1, $source); - $taskstatic = new Task($this->db); - - // Security check - $socid=0; - if ($user->socid > 0) $socid = $user->socid; - - $tasksarray=$taskstatic->getTasksArray(0, 0, $fromid, $socid, 0); - - $tab_conv_child_parent=array(); - - // Loop on each task, to clone it - foreach ($tasksarray as $tasktoclone) + foreach ($tab as $contacttoadd) { - $result_clone = $taskstatic->createFromClone($user, $tasktoclone->id, $clone_project_id, $tasktoclone->fk_parent, $move_date, true, false, $clone_task_file, true, false); - if ($result_clone <= 0) + $clone_project->add_contact($contacttoadd['id'], $contacttoadd['code'], $contacttoadd['source'], $notrigger); + if ($clone_project->error == 'DB_ERROR_RECORD_ALREADY_EXISTS') { - $this->error .= $result_clone->error; + $langs->load("errors"); + $this->error .= $langs->trans("ErrorThisContactIsAlreadyDefinedAsThisType"); $error++; } else { - $new_task_id = $result_clone; - $taskstatic->fetch($tasktoclone->id); - - //manage new parent clone task id - // if the current task has child we store the original task id and the equivalent clone task id - if (($taskstatic->hasChildren()) && !array_key_exists($tasktoclone->id, $tab_conv_child_parent)) + if ($clone_project->error != '') { - $tab_conv_child_parent[$tasktoclone->id] = $new_task_id; + $this->error .= $clone_project->error; + $error++; } } } + } + } - //Parse all clone node to be sure to update new parent - $tasksarray = $taskstatic->getTasksArray(0, 0, $clone_project_id, $socid, 0); - foreach ($tasksarray as $task_cloned) + //Duplicate file + if ($clone_project_file) + { + require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; + + $clone_project_dir = $conf->projet->dir_output."/".dol_sanitizeFileName($defaultref); + $ori_project_dir = $conf->projet->dir_output."/".dol_sanitizeFileName($orign_project_ref); + + if (dol_mkdir($clone_project_dir) >= 0) + { + $filearray = dol_dir_list($ori_project_dir, "files", 0, '', '(\.meta|_preview.*\.png)$', '', SORT_ASC, 1); + foreach ($filearray as $key => $file) { - $taskstatic->fetch($task_cloned->id); - if ($taskstatic->fk_task_parent != 0) + $rescopy = dol_copy($ori_project_dir.'/'.$file['name'], $clone_project_dir.'/'.$file['name'], 0, 1); + if (is_numeric($rescopy) && $rescopy < 0) { - $taskstatic->fk_task_parent = $tab_conv_child_parent[$taskstatic->fk_task_parent]; - } - $res = $taskstatic->update($user, $notrigger); - if ($result_clone <= 0) - { - $this->error .= $taskstatic->error; + $this->error .= $langs->trans("ErrorFailToCopyFile", $ori_project_dir.'/'.$file['name'], $clone_project_dir.'/'.$file['name']); $error++; } } } - } - - unset($clone_project->context['createfromclone']); - - if (!$error) - { - $this->db->commit(); - return $clone_project_id; - } - else - { - $this->db->rollback(); - dol_syslog(get_class($this)."::createFromClone nbError: ".$error." error : ".$this->error, LOG_ERR); - return -1; - } - } - - - /** - * Shift project task date from current date to delta - * - * @param integer $old_project_dt_start Old project start date - * @return int 1 if OK or < 0 if KO - */ - public function shiftTaskDate($old_project_dt_start) - { - global $user, $langs, $conf; - - $error=0; - - $taskstatic = new Task($this->db); - - // Security check - $socid=0; - if ($user->socid > 0) $socid = $user->socid; - - $tasksarray=$taskstatic->getTasksArray(0, 0, $this->id, $socid, 0); - - foreach ($tasksarray as $tasktoshiftdate) - { - $to_update=false; - // Fetch only if update of date will be made - if ((!empty($tasktoshiftdate->date_start)) || (!empty($tasktoshiftdate->date_end))) + else { - //dol_syslog(get_class($this)."::shiftTaskDate to_update", LOG_DEBUG); - $to_update=true; - $task = new Task($this->db); - $result = $task->fetch($tasktoshiftdate->id); - if (!$result) + $this->error .= $langs->trans('ErrorInternalErrorDetected').':dol_mkdir'; + $error++; + } + } + + //Duplicate task + if ($clone_task) + { + require_once DOL_DOCUMENT_ROOT . '/projet/class/task.class.php'; + + $taskstatic = new Task($this->db); + + // Security check + $socid=0; + if ($user->socid > 0) $socid = $user->socid; + + $tasksarray=$taskstatic->getTasksArray(0, 0, $fromid, $socid, 0); + + $tab_conv_child_parent=array(); + + // Loop on each task, to clone it + foreach ($tasksarray as $tasktoclone) + { + $result_clone = $taskstatic->createFromClone($user, $tasktoclone->id, $clone_project_id, $tasktoclone->fk_parent, $move_date, true, false, $clone_task_file, true, false); + if ($result_clone <= 0) { + $this->error .= $result_clone->error; $error++; - $this->error.=$task->error; - } - } - //print "$this->date_start + $tasktoshiftdate->date_start - $old_project_dt_start";exit; - - //Calcultate new task start date with difference between old proj start date and origin task start date - if (!empty($tasktoshiftdate->date_start)) - { - $task->date_start = $this->date_start + ($tasktoshiftdate->date_start - $old_project_dt_start); - } - - //Calcultate new task end date with difference between origin proj end date and origin task end date - if (!empty($tasktoshiftdate->date_end)) - { - $task->date_end = $this->date_start + ($tasktoshiftdate->date_end - $old_project_dt_start); - } - - if ($to_update) - { - $result = $task->update($user); - if (!$result) - { - $error++; - $this->error .= $task->error; - } - } - } - if ($error != 0) - { - return -1; - } - return $result; - } - - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Associate element to a project - * - * @param string $tableName Table of the element to update - * @param int $elementSelectId Key-rowid of the line of the element to update - * @return int 1 if OK or < 0 if KO - */ - public function update_element($tableName, $elementSelectId) - { - // phpcs:enable - $sql = "UPDATE ".MAIN_DB_PREFIX.$tableName; - - if ($tableName == "actioncomm") - { - $sql .= " SET fk_project=".$this->id; - $sql .= " WHERE id=".$elementSelectId; - } - else - { - $sql .= " SET fk_projet=".$this->id; - $sql .= " WHERE rowid=".$elementSelectId; - } - - dol_syslog(get_class($this)."::update_element", LOG_DEBUG); - $resql = $this->db->query($sql); - if (!$resql) { - $this->error = $this->db->lasterror(); - return -1; - } else { - return 1; - } - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Associate element to a project - * - * @param string $tableName Table of the element to update - * @param int $elementSelectId Key-rowid of the line of the element to update - * @param string $projectfield The column name that stores the link with the project - * - * @return int 1 if OK or < 0 if KO - */ - public function remove_element($tableName, $elementSelectId, $projectfield = 'fk_projet') - { - // phpcs:enable - $sql = "UPDATE ".MAIN_DB_PREFIX.$tableName; - - if ($tableName == "actioncomm") - { - $sql .= " SET fk_project=NULL"; - $sql .= " WHERE id=".$elementSelectId; - } else - { - $sql .= " SET ".$projectfield."=NULL"; - $sql .= " WHERE rowid=".$elementSelectId; - } - - dol_syslog(get_class($this)."::remove_element", LOG_DEBUG); - $resql = $this->db->query($sql); - if (!$resql) { - $this->error = $this->db->lasterror(); - return -1; - } else { - return 1; - } - } - - /** - * Create an intervention document on disk using template defined into PROJECT_ADDON_PDF - * - * @param string $modele Force template to use ('' by default) - * @param Translate $outputlangs Objet lang to use for translation - * @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,$langs; - - $langs->load("projects"); - - if (! dol_strlen($modele)) { - $modele = 'baleine'; - - if ($this->modelpdf) { - $modele = $this->modelpdf; - } elseif (! empty($conf->global->PROJECT_ADDON_PDF)) { - $modele = $conf->global->PROJECT_ADDON_PDF; - } - } - - $modelpath = "core/modules/project/doc/"; - - return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref); - } - - - /** - * Load time spent into this->weekWorkLoad and this->weekWorkLoadPerTask for all day of a week of project. - * Note: array weekWorkLoad and weekWorkLoadPerTask are reset and filled at each call. - * - * @param int $datestart First day of week (use dol_get_first_day to find this date) - * @param int $taskid Filter on a task id - * @param int $userid Time spent by a particular user - * @return int <0 if OK, >0 if KO - */ - public function loadTimeSpent($datestart, $taskid = 0, $userid = 0) - { - $error=0; - - $this->weekWorkLoad=array(); - $this->weekWorkLoadPerTask=array(); - - if (empty($datestart)) dol_print_error('', 'Error datestart parameter is empty'); - - $sql = "SELECT ptt.rowid as taskid, ptt.task_duration, ptt.task_date, ptt.task_datehour, ptt.fk_task"; - $sql.= " FROM ".MAIN_DB_PREFIX."projet_task_time AS ptt, ".MAIN_DB_PREFIX."projet_task as pt"; - $sql.= " WHERE ptt.fk_task = pt.rowid"; - $sql.= " AND pt.fk_projet = ".$this->id; - $sql.= " AND (ptt.task_date >= '".$this->db->idate($datestart)."' "; - $sql.= " AND ptt.task_date <= '".$this->db->idate(dol_time_plus_duree($datestart, 1, 'w') - 1)."')"; - if ($taskid) $sql.= " AND ptt.fk_task=".$taskid; - if (is_numeric($userid)) $sql.= " AND ptt.fk_user=".$userid; - - //print $sql; - $resql=$this->db->query($sql); - if ($resql) - { - $daylareadyfound=array(); - - $num = $this->db->num_rows($resql); - $i = 0; - // Loop on each record found, so each couple (project id, task id) - while ($i < $num) - { - $obj=$this->db->fetch_object($resql); - $day=$this->db->jdate($obj->task_date); // task_date is date without hours - if (empty($daylareadyfound[$day])) - { - $this->weekWorkLoad[$day] = $obj->task_duration; - $this->weekWorkLoadPerTask[$day][$obj->fk_task] = $obj->task_duration; } else { - $this->weekWorkLoad[$day] += $obj->task_duration; - $this->weekWorkLoadPerTask[$day][$obj->fk_task] += $obj->task_duration; - } - $daylareadyfound[$day]=1; - $i++; - } - $this->db->free($resql); - return 1; - } - else - { - $this->error = "Error ".$this->db->lasterror(); - dol_syslog(get_class($this)."::fetch ".$this->error, LOG_ERR); - return -1; - } - } + $new_task_id = $result_clone; + $taskstatic->fetch($tasktoclone->id); - - // 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; - - $projectsListId = null; - if (!$user->rights->projet->all->lire) $projectsListId = $this->getProjectsAuthorizedForUser($user, 0, 1); - - $sql = "SELECT p.rowid, p.fk_statut as status, p.fk_opp_status, p.datee as datee"; - $sql .= " FROM (".MAIN_DB_PREFIX."projet as p"; - $sql .= ")"; - $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s on p.fk_soc = s.rowid"; - // For external user, no check is done on company permission because readability is managed by public status of project and assignement. - //if (! $user->rights->societe->client->voir && ! $socid) $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON sc.fk_soc = s.rowid"; - $sql .= " WHERE p.fk_statut = 1"; - $sql .= " AND p.entity IN (".getEntity('project').')'; - if (!empty($projectsListId)) $sql .= " AND p.rowid IN (".$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 = ".$socid.")"; - // For external user, no check is done on company permission because readability is managed by public status of project and assignement. - //if (! $user->rights->societe->client->voir && ! $socid) $sql.= " AND ((s.rowid = sc.fk_soc AND sc.fk_user = " .$user->id.") OR (s.rowid IS NULL))"; - - //print $sql; - $resql = $this->db->query($sql); - if ($resql) - { - $project_static = new Project($this->db); - - $response = new WorkboardResponse(); - $response->warning_delay = $conf->projet->warning_delay / 60 / 60 / 24; - $response->label = $langs->trans("OpenedProjects"); - $response->labelShort = $langs->trans("Opened"); - if ($user->rights->projet->all->lire) $response->url = DOL_URL_ROOT.'/projet/list.php?search_status=1&mainmenu=project'; - else $response->url = DOL_URL_ROOT.'/projet/list.php?search_project_user=-1&search_status=1&mainmenu=project'; - $response->img = img_object('', "projectpub"); - - // This assignment in condition is not a bug. It allows walking the results. - while ($obj = $this->db->fetch_object($resql)) - { - $response->nbtodo++; - - $project_static->statut = $obj->status; - $project_static->opp_status = $obj->opp_status; - $project_static->datee = $this->db->jdate($obj->datee); - - if ($project_static->hasDelay()) { - $response->nbtodolate++; + //manage new parent clone task id + // if the current task has child we store the original task id and the equivalent clone task id + if (($taskstatic->hasChildren()) && !array_key_exists($tasktoclone->id, $tab_conv_child_parent)) + { + $tab_conv_child_parent[$tasktoclone->id] = $new_task_id; + } } } - return $response; - } - else - { - $this->error = $this->db->error(); - return -1; - } - } - - - /** - * Function used to replace a thirdparty id with another one. - * - * @param DoliDB $db Database handler - * @param int $origin_id Old thirdparty id - * @param int $dest_id New thirdparty id - * @return bool - */ - public static function replaceThirdparty(DoliDB $db, $origin_id, $dest_id) - { - $tables = array( - 'projet' - ); - - return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables); - } - - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Charge indicateurs this->nb pour le tableau de bord - * - * @return int <0 if KO, >0 if OK - */ - public function load_state_board() - { - // phpcs:enable - global $user; - - $this->nb = array(); - - $sql = "SELECT count(p.rowid) as nb"; - $sql .= " FROM ".MAIN_DB_PREFIX."projet as p"; - $sql .= " WHERE"; - $sql .= " p.entity IN (".getEntity('project').")"; - if (!$user->rights->projet->all->lire) - { - $projectsListId = $this->getProjectsAuthorizedForUser($user, 0, 1); - $sql .= "AND p.rowid IN (".$projectsListId.")"; - } - - $resql = $this->db->query($sql); - if ($resql) - { - while ($obj = $this->db->fetch_object($resql)) + //Parse all clone node to be sure to update new parent + $tasksarray = $taskstatic->getTasksArray(0, 0, $clone_project_id, $socid, 0); + foreach ($tasksarray as $task_cloned) { - $this->nb["projects"] = $obj->nb; - } - $this->db->free($resql); - return 1; - } - else - { - dol_print_error($this->db); - $this->error = $this->db->error(); - return -1; - } - } - - - /** - * Is the project delayed? - * - * @return bool - */ - public function hasDelay() - { - global $conf; - - if (!($this->statut == self::STATUS_VALIDATED)) return false; - if (!$this->datee && !$this->date_end) return false; - - $now = dol_now(); - - return ($this->datee ? $this->datee : $this->date_end) < ($now - $conf->projet->warning_delay); - } - - - /** - * Charge les informations d'ordre info dans l'objet commande - * - * @param int $id Id of order - * @return void - */ - public function info($id) - { - $sql = 'SELECT c.rowid, datec as datec, tms as datem,'; - $sql .= ' date_close as datecloture,'; - $sql .= ' fk_user_creat as fk_user_author, fk_user_close as fk_use_cloture'; - $sql .= ' FROM '.MAIN_DB_PREFIX.'projet as c'; - $sql .= ' WHERE c.rowid = '.$id; - $result = $this->db->query($sql); - if ($result) - { - if ($this->db->num_rows($result)) - { - $obj = $this->db->fetch_object($result); - $this->id = $obj->rowid; - if ($obj->fk_user_author) + $taskstatic->fetch($task_cloned->id); + if ($taskstatic->fk_task_parent != 0) { - $cuser = new User($this->db); - $cuser->fetch($obj->fk_user_author); - $this->user_creation = $cuser; + $taskstatic->fk_task_parent = $tab_conv_child_parent[$taskstatic->fk_task_parent]; } - - if ($obj->fk_user_cloture) + $res = $taskstatic->update($user, $notrigger); + if ($result_clone <= 0) { - $cluser = new User($this->db); - $cluser->fetch($obj->fk_user_cloture); - $this->user_cloture = $cluser; + $this->error .= $taskstatic->error; + $error++; } - - $this->date_creation = $this->db->jdate($obj->datec); - $this->date_modification = $this->db->jdate($obj->datem); - $this->date_cloture = $this->db->jdate($obj->datecloture); } - - $this->db->free($result); - } - else - { - dol_print_error($this->db); } } - /** - * Sets object to supplied categories. - * - * Deletes object from existing categories not supplied. - * Adds it to non existing supplied categories. - * Existing categories are left untouch. - * - * @param int[]|int $categories Category or categories IDs - * @return void - */ - public function setCategories($categories) + unset($clone_project->context['createfromclone']); + + if (!$error) { - $type_categ = Categorie::TYPE_PROJECT; + $this->db->commit(); + return $clone_project_id; + } + else + { + $this->db->rollback(); + dol_syslog(get_class($this)."::createFromClone nbError: ".$error." error : ".$this->error, LOG_ERR); + return -1; + } + } - // Handle single category - if (!is_array($categories)) { - $categories = array($categories); - } - // Get current categories - require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php'; - $c = new Categorie($this->db); - $existing = $c->containing($this->id, $type_categ, 'id'); + /** + * Shift project task date from current date to delta + * + * @param integer $old_project_dt_start Old project start date + * @return int 1 if OK or < 0 if KO + */ + public function shiftTaskDate($old_project_dt_start) + { + global $user, $langs, $conf; - // Diff - if (is_array($existing)) { - $to_del = array_diff($existing, $categories); - $to_add = array_diff($categories, $existing); - } else { - $to_del = array(); // Nothing to delete - $to_add = $categories; - } + $error=0; - // Process - foreach ($to_del as $del) { - if ($c->fetch($del) > 0) { - $result = $c->del_type($this, $type_categ); - if ($result < 0) { - $this->errors = $c->errors; - $this->error = $c->error; - return -1; - } + $taskstatic = new Task($this->db); + + // Security check + $socid=0; + if ($user->socid > 0) $socid = $user->socid; + + $tasksarray=$taskstatic->getTasksArray(0, 0, $this->id, $socid, 0); + + foreach ($tasksarray as $tasktoshiftdate) + { + $to_update=false; + // Fetch only if update of date will be made + if ((!empty($tasktoshiftdate->date_start)) || (!empty($tasktoshiftdate->date_end))) + { + //dol_syslog(get_class($this)."::shiftTaskDate to_update", LOG_DEBUG); + $to_update=true; + $task = new Task($this->db); + $result = $task->fetch($tasktoshiftdate->id); + if (!$result) + { + $error++; + $this->error.=$task->error; } } - foreach ($to_add as $add) { - if ($c->fetch($add) > 0) { - $result = $c->add_type($this, $type_categ); - if ($result < 0) { - $this->errors = $c->errors; - $this->error = $c->error; - return -1; - } - } + //print "$this->date_start + $tasktoshiftdate->date_start - $old_project_dt_start";exit; + + //Calcultate new task start date with difference between old proj start date and origin task start date + if (!empty($tasktoshiftdate->date_start)) + { + $task->date_start = $this->date_start + ($tasktoshiftdate->date_start - $old_project_dt_start); } + //Calcultate new task end date with difference between origin proj end date and origin task end date + if (!empty($tasktoshiftdate->date_end)) + { + $task->date_end = $this->date_start + ($tasktoshiftdate->date_end - $old_project_dt_start); + } + + if ($to_update) + { + $result = $task->update($user); + if (!$result) + { + $error++; + $this->error .= $task->error; + } + } + } + if ($error != 0) + { + return -1; + } + return $result; + } + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Associate element to a project + * + * @param string $tableName Table of the element to update + * @param int $elementSelectId Key-rowid of the line of the element to update + * @return int 1 if OK or < 0 if KO + */ + public function update_element($tableName, $elementSelectId) + { + // phpcs:enable + $sql = "UPDATE ".MAIN_DB_PREFIX.$tableName; + + if ($tableName == "actioncomm") + { + $sql .= " SET fk_project=".$this->id; + $sql .= " WHERE id=".$elementSelectId; + } + else + { + $sql .= " SET fk_projet=".$this->id; + $sql .= " WHERE rowid=".$elementSelectId; + } + + dol_syslog(get_class($this)."::update_element", LOG_DEBUG); + $resql = $this->db->query($sql); + if (!$resql) { + $this->error = $this->db->lasterror(); + return -1; + } else { return 1; } + } + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Associate element to a project + * + * @param string $tableName Table of the element to update + * @param int $elementSelectId Key-rowid of the line of the element to update + * @param string $projectfield The column name that stores the link with the project + * + * @return int 1 if OK or < 0 if KO + */ + public function remove_element($tableName, $elementSelectId, $projectfield = 'fk_projet') + { + // phpcs:enable + $sql = "UPDATE ".MAIN_DB_PREFIX.$tableName; - /** - * Create an array of tasks of current project - * - * @param User $user Object user we want project allowed to - * @return int >0 if OK, <0 if KO - */ - public function getLinesArray($user) + if ($tableName == "actioncomm") { - require_once DOL_DOCUMENT_ROOT.'/projet/class/task.class.php'; - $taskstatic = new Task($this->db); - - $this->lines = $taskstatic->getTasksArray(0, $user, $this->id, 0, 0); + $sql .= " SET fk_project=NULL"; + $sql .= " WHERE id=".$elementSelectId; + } else + { + $sql .= " SET ".$projectfield."=NULL"; + $sql .= " WHERE rowid=".$elementSelectId; } + + dol_syslog(get_class($this)."::remove_element", LOG_DEBUG); + $resql = $this->db->query($sql); + if (!$resql) { + $this->error = $this->db->lasterror(); + return -1; + } else { + return 1; + } + } + + /** + * Create an intervention document on disk using template defined into PROJECT_ADDON_PDF + * + * @param string $modele Force template to use ('' by default) + * @param Translate $outputlangs Objet lang to use for translation + * @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,$langs; + + $langs->load("projects"); + + if (! dol_strlen($modele)) { + $modele = 'baleine'; + + if ($this->modelpdf) { + $modele = $this->modelpdf; + } elseif (! empty($conf->global->PROJECT_ADDON_PDF)) { + $modele = $conf->global->PROJECT_ADDON_PDF; + } + } + + $modelpath = "core/modules/project/doc/"; + + return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref); + } + + + /** + * Load time spent into this->weekWorkLoad and this->weekWorkLoadPerTask for all day of a week of project. + * Note: array weekWorkLoad and weekWorkLoadPerTask are reset and filled at each call. + * + * @param int $datestart First day of week (use dol_get_first_day to find this date) + * @param int $taskid Filter on a task id + * @param int $userid Time spent by a particular user + * @return int <0 if OK, >0 if KO + */ + public function loadTimeSpent($datestart, $taskid = 0, $userid = 0) + { + $error=0; + + $this->weekWorkLoad=array(); + $this->weekWorkLoadPerTask=array(); + + if (empty($datestart)) dol_print_error('', 'Error datestart parameter is empty'); + + $sql = "SELECT ptt.rowid as taskid, ptt.task_duration, ptt.task_date, ptt.task_datehour, ptt.fk_task"; + $sql.= " FROM ".MAIN_DB_PREFIX."projet_task_time AS ptt, ".MAIN_DB_PREFIX."projet_task as pt"; + $sql.= " WHERE ptt.fk_task = pt.rowid"; + $sql.= " AND pt.fk_projet = ".$this->id; + $sql.= " AND (ptt.task_date >= '".$this->db->idate($datestart)."' "; + $sql.= " AND ptt.task_date <= '".$this->db->idate(dol_time_plus_duree($datestart, 1, 'w') - 1)."')"; + if ($taskid) $sql.= " AND ptt.fk_task=".$taskid; + if (is_numeric($userid)) $sql.= " AND ptt.fk_user=".$userid; + + //print $sql; + $resql=$this->db->query($sql); + if ($resql) + { + $daylareadyfound=array(); + + $num = $this->db->num_rows($resql); + $i = 0; + // Loop on each record found, so each couple (project id, task id) + while ($i < $num) + { + $obj=$this->db->fetch_object($resql); + $day=$this->db->jdate($obj->task_date); // task_date is date without hours + if (empty($daylareadyfound[$day])) + { + $this->weekWorkLoad[$day] = $obj->task_duration; + $this->weekWorkLoadPerTask[$day][$obj->fk_task] = $obj->task_duration; + } + else + { + $this->weekWorkLoad[$day] += $obj->task_duration; + $this->weekWorkLoadPerTask[$day][$obj->fk_task] += $obj->task_duration; + } + $daylareadyfound[$day]=1; + $i++; + } + $this->db->free($resql); + return 1; + } + else + { + $this->error = "Error ".$this->db->lasterror(); + dol_syslog(get_class($this)."::fetch ".$this->error, LOG_ERR); + return -1; + } + } + + + // 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; + + $projectsListId = null; + if (!$user->rights->projet->all->lire) $projectsListId = $this->getProjectsAuthorizedForUser($user, 0, 1); + + $sql = "SELECT p.rowid, p.fk_statut as status, p.fk_opp_status, p.datee as datee"; + $sql .= " FROM (".MAIN_DB_PREFIX."projet as p"; + $sql .= ")"; + $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s on p.fk_soc = s.rowid"; + // For external user, no check is done on company permission because readability is managed by public status of project and assignement. + //if (! $user->rights->societe->client->voir && ! $socid) $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON sc.fk_soc = s.rowid"; + $sql .= " WHERE p.fk_statut = 1"; + $sql .= " AND p.entity IN (".getEntity('project').')'; + if (!empty($projectsListId)) $sql .= " AND p.rowid IN (".$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 = ".$socid.")"; + // For external user, no check is done on company permission because readability is managed by public status of project and assignement. + //if (! $user->rights->societe->client->voir && ! $socid) $sql.= " AND ((s.rowid = sc.fk_soc AND sc.fk_user = " .$user->id.") OR (s.rowid IS NULL))"; + + //print $sql; + $resql = $this->db->query($sql); + if ($resql) + { + $project_static = new Project($this->db); + + $response = new WorkboardResponse(); + $response->warning_delay = $conf->projet->warning_delay / 60 / 60 / 24; + $response->label = $langs->trans("OpenedProjects"); + $response->labelShort = $langs->trans("Opened"); + if ($user->rights->projet->all->lire) $response->url = DOL_URL_ROOT.'/projet/list.php?search_status=1&mainmenu=project'; + else $response->url = DOL_URL_ROOT.'/projet/list.php?search_project_user=-1&search_status=1&mainmenu=project'; + $response->img = img_object('', "projectpub"); + + // This assignment in condition is not a bug. It allows walking the results. + while ($obj = $this->db->fetch_object($resql)) + { + $response->nbtodo++; + + $project_static->statut = $obj->status; + $project_static->opp_status = $obj->opp_status; + $project_static->datee = $this->db->jdate($obj->datee); + + if ($project_static->hasDelay()) { + $response->nbtodolate++; + } + } + + return $response; + } + else + { + $this->error = $this->db->error(); + return -1; + } + } + + + /** + * Function used to replace a thirdparty id with another one. + * + * @param DoliDB $db Database handler + * @param int $origin_id Old thirdparty id + * @param int $dest_id New thirdparty id + * @return bool + */ + public static function replaceThirdparty(DoliDB $db, $origin_id, $dest_id) + { + $tables = array( + 'projet' + ); + + return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables); + } + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Charge indicateurs this->nb pour le tableau de bord + * + * @return int <0 if KO, >0 if OK + */ + public function load_state_board() + { + // phpcs:enable + global $user; + + $this->nb = array(); + + $sql = "SELECT count(p.rowid) as nb"; + $sql .= " FROM ".MAIN_DB_PREFIX."projet as p"; + $sql .= " WHERE"; + $sql .= " p.entity IN (".getEntity('project').")"; + if (!$user->rights->projet->all->lire) + { + $projectsListId = $this->getProjectsAuthorizedForUser($user, 0, 1); + $sql .= "AND p.rowid IN (".$projectsListId.")"; + } + + $resql = $this->db->query($sql); + if ($resql) + { + while ($obj = $this->db->fetch_object($resql)) + { + $this->nb["projects"] = $obj->nb; + } + $this->db->free($resql); + return 1; + } + else + { + dol_print_error($this->db); + $this->error = $this->db->error(); + return -1; + } + } + + + /** + * Is the project delayed? + * + * @return bool + */ + public function hasDelay() + { + global $conf; + + if (!($this->statut == self::STATUS_VALIDATED)) return false; + if (!$this->datee && !$this->date_end) return false; + + $now = dol_now(); + + return ($this->datee ? $this->datee : $this->date_end) < ($now - $conf->projet->warning_delay); + } + + + /** + * Charge les informations d'ordre info dans l'objet commande + * + * @param int $id Id of order + * @return void + */ + public function info($id) + { + $sql = 'SELECT c.rowid, datec as datec, tms as datem,'; + $sql .= ' date_close as datecloture,'; + $sql .= ' fk_user_creat as fk_user_author, fk_user_close as fk_use_cloture'; + $sql .= ' FROM '.MAIN_DB_PREFIX.'projet as c'; + $sql .= ' WHERE c.rowid = '.$id; + $result = $this->db->query($sql); + if ($result) + { + if ($this->db->num_rows($result)) + { + $obj = $this->db->fetch_object($result); + $this->id = $obj->rowid; + if ($obj->fk_user_author) + { + $cuser = new User($this->db); + $cuser->fetch($obj->fk_user_author); + $this->user_creation = $cuser; + } + + if ($obj->fk_user_cloture) + { + $cluser = new User($this->db); + $cluser->fetch($obj->fk_user_cloture); + $this->user_cloture = $cluser; + } + + $this->date_creation = $this->db->jdate($obj->datec); + $this->date_modification = $this->db->jdate($obj->datem); + $this->date_cloture = $this->db->jdate($obj->datecloture); + } + + $this->db->free($result); + } + else + { + dol_print_error($this->db); + } + } + + /** + * Sets object to supplied categories. + * + * Deletes object from existing categories not supplied. + * Adds it to non existing supplied categories. + * Existing categories are left untouch. + * + * @param int[]|int $categories Category or categories IDs + * @return void + */ + public function setCategories($categories) + { + $type_categ = Categorie::TYPE_PROJECT; + + // Handle single category + if (!is_array($categories)) { + $categories = array($categories); + } + + // Get current categories + require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php'; + $c = new Categorie($this->db); + $existing = $c->containing($this->id, $type_categ, 'id'); + + // Diff + if (is_array($existing)) { + $to_del = array_diff($existing, $categories); + $to_add = array_diff($categories, $existing); + } else { + $to_del = array(); // Nothing to delete + $to_add = $categories; + } + + // Process + foreach ($to_del as $del) { + if ($c->fetch($del) > 0) { + $result = $c->del_type($this, $type_categ); + if ($result < 0) { + $this->errors = $c->errors; + $this->error = $c->error; + return -1; + } + } + } + foreach ($to_add as $add) { + if ($c->fetch($add) > 0) { + $result = $c->add_type($this, $type_categ); + if ($result < 0) { + $this->errors = $c->errors; + $this->error = $c->error; + return -1; + } + } + } + + return 1; + } + + + /** + * Create an array of tasks of current project + * + * @param User $user Object user we want project allowed to + * @return int >0 if OK, <0 if KO + */ + public function getLinesArray($user) + { + require_once DOL_DOCUMENT_ROOT.'/projet/class/task.class.php'; + $taskstatic = new Task($this->db); + + $this->lines = $taskstatic->getTasksArray(0, $user, $this->id, 0, 0); + } }