diff --git a/htdocs/compta/facture/class/facture.class.php b/htdocs/compta/facture/class/facture.class.php index ed4d8bfd498..07456b3b579 100644 --- a/htdocs/compta/facture/class/facture.class.php +++ b/htdocs/compta/facture/class/facture.class.php @@ -18,7 +18,7 @@ * Copyright (C) 2018-2022 Alexandre Spangaro * Copyright (C) 2018 Nicolas ZABOURI * Copyright (C) 2022 Sylvain Legrand - * Copyright (C) 2022 Gauthier VERDOL + * Copyright (C) 2023 Gauthier VERDOL * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -2690,7 +2690,7 @@ class Facture extends CommonInvoice return -5; } - $sql = 'UPDATE '.MAIN_DB_PREFIX.'projet_task_time'; + $sql = 'UPDATE '.MAIN_DB_PREFIX.'element_time'; $sql .= ' SET invoice_id = NULL, invoice_line_id = NULL'; $sql .= ' WHERE invoice_id = '.((int) $rowid); diff --git a/htdocs/core/boxes/box_validated_projects.php b/htdocs/core/boxes/box_validated_projects.php index bab64d5a000..f071871d0d4 100644 --- a/htdocs/core/boxes/box_validated_projects.php +++ b/htdocs/core/boxes/box_validated_projects.php @@ -4,6 +4,7 @@ * Copyright (C) 2015 Frederic France * Copyright (C) 2016 Juan José Menent * Copyright (C) 2020 Pierre Ardoin + * Copyright (C) 2023 Gauthier VERDOL * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -119,7 +120,7 @@ class box_validated_projects extends ModeleBoxes if ($projectsListId) { $sql .= ' AND p.rowid IN ('.$this->db->sanitize($projectsListId).')'; // Only projects that are allowed } - $sql .= " AND t.rowid NOT IN (SELECT fk_task FROM ".MAIN_DB_PREFIX."projet_task_time WHERE fk_user = ".((int) $user->id).")"; + $sql .= " AND t.rowid NOT IN (SELECT fk_element FROM ".MAIN_DB_PREFIX."element_time WHERE elementtype = 'task' AND fk_user = ".((int) $user->id).")"; $sql .= " GROUP BY p.rowid, p.ref, p.fk_soc, p.dateo"; $sql .= " ORDER BY p.dateo ASC"; diff --git a/htdocs/core/class/commonobject.class.php b/htdocs/core/class/commonobject.class.php index e3c3dff2b7c..eabcfda8491 100644 --- a/htdocs/core/class/commonobject.class.php +++ b/htdocs/core/class/commonobject.class.php @@ -14,7 +14,7 @@ * Copyright (C) 2017 Rui Strecht * Copyright (C) 2018-2021 Frédéric France * Copyright (C) 2018 Josep Lluís Amador - * Copyright (C) 2021 Gauthier VERDOL + * Copyright (C) 2023 Gauthier VERDOL * Copyright (C) 2021 Grégory Blémand * Copyright (C) 2023 Lenin Rivas * @@ -4637,6 +4637,9 @@ abstract class CommonObject if (!empty($element['parent']) && !empty($element['parentkey'])) { $sql.= " AND c.".$element['parentkey']." = p.rowid"; } + if (!empty($element['parent']) && !empty($element['parenttypefield']) && !empty($element['parenttypevalue'])) { + $sql.= " AND c.".$element['parenttypefield']." = '".$element['parenttypevalue']."'"; + } if (!empty($entity)) { if (!empty($element['parent']) && !empty($element['parentkey'])) { $sql.= " AND p.entity = ".((int) $entity); diff --git a/htdocs/core/class/timespent.class.php b/htdocs/core/class/timespent.class.php new file mode 100755 index 00000000000..f6ae2b45c71 --- /dev/null +++ b/htdocs/core/class/timespent.class.php @@ -0,0 +1,1134 @@ + + * Copyright (C) 2023 Frédéric France + * Copyright (C) 2023 Gauthier VERDOL + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file class/timespent.class.php + * \ingroup timespent + * \brief This file is a CRUD class file for TimeSpent (Create/Read/Update/Delete) + */ + +// Put here all includes required by your class file +require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php'; +//require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php'; +//require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php'; + +/** + * Class for TimeSpent + */ +class TimeSpent extends CommonObject +{ + /** + * @var string ID of module. + */ + public $module = 'timespent'; + + /** + * @var string ID to identify managed object. + */ + public $element = 'timespent'; + + /** + * @var string Name of table without prefix where object is stored. This is also the key used for extrafields management. + */ + public $table_element = 'element_time'; + + /** + * @var int Does this object support multicompany module ? + * 0=No test on entity, 1=Test with field entity, 'field@table'=Test with link by field@table + */ + public $ismultientitymanaged = 0; + + /** + * @var int Does object support extrafields ? 0=No, 1=Yes + */ + public $isextrafieldmanaged = 0; + + /** + * @var string String with name of icon for timespent. Must be a 'fa-xxx' fontawesome code (or 'fa-xxx_fa_color_size') or 'timespent@timespent' if picto is file 'img/object_timespent.png'. + */ + public $picto = 'fa-file'; + + + const STATUS_DRAFT = 0; + const STATUS_VALIDATED = 1; + const STATUS_CANCELED = 9; + + + /** + * 'type' field format: + * 'integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]', + * 'select' (list of values are in 'options'), + * 'sellist:TableName:LabelFieldName[:KeyFieldName[:KeyFieldParent[:Filter[:Sortfield]]]]', + * 'chkbxlst:...', + * 'varchar(x)', + * 'text', 'text:none', 'html', + * 'double(24,8)', 'real', 'price', + * 'date', 'datetime', 'timestamp', 'duration', + * 'boolean', 'checkbox', 'radio', 'array', + * 'mail', 'phone', 'url', 'password', 'ip' + * Note: Filter must be a Dolibarr filter syntax string. Example: "(t.ref:like:'SO-%') or (t.date_creation:<:'20160101') or (t.status:!=:0) or (t.nature:is:NULL)" + * 'label' the translation key. + * 'picto' is code of a picto to show before value in forms + * 'enabled' is a condition when the field must be managed (Example: 1 or '$conf->global->MY_SETUP_PARAM' or 'isModEnabled("multicurrency")' ...) + * 'position' is the sort order of field. + * 'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0). + * 'visible' says if field is visible in list (Examples: 0=Not visible, 1=Visible on list and create/update/view forms, 2=Visible on list only, 3=Visible on create/update/view form only (not list), 4=Visible on list and update/view form only (not create). 5=Visible on list and view only (not create/not update). Using a negative value means field is not shown by default on list but can be selected for viewing) + * 'noteditable' says if field is not editable (1 or 0) + * 'alwayseditable' says if field can be modified also when status is not draft ('1' or '0') + * 'default' is a default value for creation (can still be overwrote by the Setup of Default Values if field is editable in creation form). Note: If default is set to '(PROV)' and field is 'ref', the default value will be set to '(PROVid)' where id is rowid when a new record is created. + * 'index' if we want an index in database. + * 'foreignkey'=>'tablename.field' if the field is a foreign key (it is recommanded to name the field fk_...). + * 'searchall' is 1 if we want to search in this field when making a search from the quick search button. + * 'isameasure' must be set to 1 or 2 if field can be used for measure. Field type must be summable like integer or double(24,8). Use 1 in most cases, or 2 if you don't want to see the column total into list (for example for percentage) + * 'css' and 'cssview' and 'csslist' is the CSS style to use on field. 'css' is used in creation and update. 'cssview' is used in view mode. 'csslist' is used for columns in lists. For example: 'css'=>'minwidth300 maxwidth500 widthcentpercentminusx', 'cssview'=>'wordbreak', 'csslist'=>'tdoverflowmax200' + * 'help' and 'helplist' is a 'TranslationString' to use to show a tooltip on field. You can also use 'TranslationString:keyfortooltiponlick' for a tooltip on click. + * 'showoncombobox' if value of the field must be visible into the label of the combobox that list record + * 'disabled' is 1 if we want to have the field locked by a 'disabled' attribute. In most cases, this is never set into the definition of $fields into class, but is set dynamically by some part of code. + * 'arrayofkeyval' to set a list of values if type is a list of predefined values. For example: array("0"=>"Draft","1"=>"Active","-1"=>"Cancel"). Note that type can be 'integer' or 'varchar' + * 'autofocusoncreate' to have field having the focus on a create form. Only 1 field should have this property set to 1. + * 'comment' is not used. You can store here any text of your choice. It is not used by application. + * 'validate' is 1 if need to validate with $this->validateField() + * 'copytoclipboard' is 1 or 2 to allow to add a picto to copy value into clipboard (1=picto after label, 2=picto after value) + * + * Note: To have value dynamic, you can set value to 0 in definition and edit the value on the fly into the constructor. + */ + + // BEGIN MODULEBUILDER PROPERTIES + /** + * @var array Array with all fields and their property. Do not use it as a static var. It may be modified by constructor. + */ + public $fields=array( + 'rowid' => array('type'=>'integer', 'label'=>'TechnicalID', 'enabled'=>'1', 'position'=>1, 'notnull'=>1, 'visible'=>0, 'noteditable'=>'1', 'index'=>1, 'css'=>'left', 'comment'=>"Id"), + 'tms' => array('type'=>'timestamp', 'label'=>'DateModification', 'enabled'=>'1', 'position'=>17, 'notnull'=>0, 'visible'=>-2,), + 'import_key' => array('type'=>'varchar(14)', 'label'=>'import_key', 'enabled'=>'1', 'position'=>15, 'notnull'=>0, 'visible'=>-1,), + 'fk_element' => array('type'=>'integer', 'label'=>'fk_element', 'enabled'=>'1', 'position'=>2, 'notnull'=>1, 'visible'=>-1,), + 'elementtype' => array('type'=>'varchar(32)', 'label'=>'elementtype', 'enabled'=>'1', 'position'=>3, 'notnull'=>1, 'visible'=>-1,), + 'element_date' => array('type'=>'date', 'label'=>'element_date', 'enabled'=>'1', 'position'=>4, 'notnull'=>0, 'visible'=>-1,), + 'element_datehour' => array('type'=>'datetime', 'label'=>'element_datehour', 'enabled'=>'1', 'position'=>5, 'notnull'=>0, 'visible'=>-1,), + 'element_date_withhour' => array('type'=>'integer', 'label'=>'element_date_withhour', 'enabled'=>'1', 'position'=>6, 'notnull'=>0, 'visible'=>-1,), + 'element_duration' => array('type'=>'double', 'label'=>'element_duration', 'enabled'=>'1', 'position'=>7, 'notnull'=>0, 'visible'=>-1,), + 'fk_product' => array('type'=>'integer', 'label'=>'fk_product', 'enabled'=>'1', 'position'=>8, 'notnull'=>0, 'visible'=>-1,), + 'fk_user' => array('type'=>'integer', 'label'=>'fk_user', 'enabled'=>'1', 'position'=>9, 'notnull'=>0, 'visible'=>-1,), + 'thm' => array('type'=>'double(24,8)', 'label'=>'thm', 'enabled'=>'1', 'position'=>10, 'notnull'=>0, 'visible'=>-1,), + 'invoice_id' => array('type'=>'integer', 'label'=>'invoice_id', 'enabled'=>'1', 'position'=>11, 'notnull'=>0, 'visible'=>-1, 'default'=>'NULL',), + 'invoice_line_id' => array('type'=>'integer', 'label'=>'invoice_line_id', 'enabled'=>'1', 'position'=>12, 'notnull'=>0, 'visible'=>-1, 'default'=>'NULL',), + 'intervention_id' => array('type'=>'integer', 'label'=>'intervention_id', 'enabled'=>'1', 'position'=>13, 'notnull'=>0, 'visible'=>-1, 'default'=>'NULL',), + 'intervention_line_id' => array('type'=>'integer', 'label'=>'intervention_line_id', 'enabled'=>'1', 'position'=>14, 'notnull'=>0, 'visible'=>-1, 'default'=>'NULL',), + 'datec' => array('type'=>'datetime', 'label'=>'datec', 'enabled'=>'1', 'position'=>16, 'notnull'=>0, 'visible'=>-1,), + 'note' => array('type'=>'text', 'label'=>'note', 'enabled'=>'1', 'position'=>18, 'notnull'=>0, 'visible'=>-1,), + ); + public $rowid; + public $tms; + public $import_key; + public $fk_element; + public $elementtype; + public $element_date; + public $element_datehour; + public $element_date_withhour; + public $element_duration; + public $fk_product; + public $fk_user; + public $thm; + public $invoice_id; + public $invoice_line_id; + public $intervention_id; + public $intervention_line_id; + public $datec; + public $note; + // END MODULEBUILDER PROPERTIES + + + // If this object has a subtable with lines + + // /** + // * @var string Name of subtable line + // */ + // public $table_element_line = 'timespent_timespentline'; + + // /** + // * @var string Field with ID of parent key if this object has a parent + // */ + // public $fk_element = 'fk_timespent'; + + // /** + // * @var string Name of subtable class that manage subtable lines + // */ + // public $class_element_line = 'TimeSpentline'; + + // /** + // * @var array List of child tables. To test if we can delete object. + // */ + // protected $childtables = array(); + + // /** + // * @var array List of child tables. To know object to delete on cascade. + // * If name matches '@ClassNAme:FilePathClass;ParentFkFieldName' it will + // * call method deleteByParentField(parentId, ParentFkFieldName) to fetch and delete child object + // */ + // protected $childtablesoncascade = array('timespent_timespentdet'); + + // /** + // * @var TimeSpentLine[] Array of subtable lines + // */ + // public $lines = array(); + + + + /** + * Constructor + * + * @param DoliDb $db Database handler + */ + public function __construct(DoliDB $db) + { + global $conf, $langs; + + $this->db = $db; + + if (empty($conf->global->MAIN_SHOW_TECHNICAL_ID) && isset($this->fields['rowid']) && !empty($this->fields['ref'])) { + $this->fields['rowid']['visible'] = 0; + } + if (!isModEnabled('multicompany') && isset($this->fields['entity'])) { + $this->fields['entity']['enabled'] = 0; + } + + // Example to show how to set values of fields definition dynamically + /*if ($user->rights->timespent->timespent->read) { + $this->fields['myfield']['visible'] = 1; + $this->fields['myfield']['noteditable'] = 0; + }*/ + + // Unset fields that are disabled + foreach ($this->fields as $key => $val) { + if (isset($val['enabled']) && empty($val['enabled'])) { + unset($this->fields[$key]); + } + } + + // Translate some data of arrayofkeyval + if (is_object($langs)) { + foreach ($this->fields as $key => $val) { + if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) { + foreach ($val['arrayofkeyval'] as $key2 => $val2) { + $this->fields[$key]['arrayofkeyval'][$key2] = $langs->trans($val2); + } + } + } + } + } + + /** + * Create object into database + * + * @param User $user User that creates + * @param bool $notrigger false=launch triggers after, true=disable triggers + * @return int <0 if KO, Id of created object if OK + */ + public function create(User $user, $notrigger = false) + { + $resultcreate = $this->createCommon($user, $notrigger); + + //$resultvalidate = $this->validate($user, $notrigger); + + return $resultcreate; + } + + /** + * Clone an object into another one + * + * @param User $user User that creates + * @param int $fromid Id of object to clone + * @return mixed New object created, <0 if KO + */ + public function createFromClone(User $user, $fromid) + { + global $langs, $extrafields; + $error = 0; + + dol_syslog(__METHOD__, LOG_DEBUG); + + $object = new self($this->db); + + $this->db->begin(); + + // Load source object + $result = $object->fetchCommon($fromid); + if ($result > 0 && !empty($object->table_element_line)) { + $object->fetchLines(); + } + + // get lines so they will be clone + //foreach($this->lines as $line) + // $line->fetch_optionals(); + + // Reset some properties + unset($object->id); + unset($object->fk_user_creat); + unset($object->import_key); + + // Clear fields + if (property_exists($object, 'ref')) { + $object->ref = empty($this->fields['ref']['default']) ? "Copy_Of_".$object->ref : $this->fields['ref']['default']; + } + if (property_exists($object, 'label')) { + $object->label = empty($this->fields['label']['default']) ? $langs->trans("CopyOf")." ".$object->label : $this->fields['label']['default']; + } + if (property_exists($object, 'status')) { + $object->status = self::STATUS_DRAFT; + } + if (property_exists($object, 'date_creation')) { + $object->date_creation = dol_now(); + } + if (property_exists($object, 'date_modification')) { + $object->date_modification = null; + } + // ... + // Clear extrafields that are unique + if (is_array($object->array_options) && count($object->array_options) > 0) { + $extrafields->fetch_name_optionals_label($this->table_element); + foreach ($object->array_options as $key => $option) { + $shortkey = preg_replace('/options_/', '', $key); + if (!empty($extrafields->attributes[$this->table_element]['unique'][$shortkey])) { + //var_dump($key); + //var_dump($clonedObj->array_options[$key]); exit; + unset($object->array_options[$key]); + } + } + } + + // Create clone + $object->context['createfromclone'] = 'createfromclone'; + $result = $object->createCommon($user); + if ($result < 0) { + $error++; + $this->error = $object->error; + $this->errors = $object->errors; + } + + if (!$error) { + // copy internal contacts + if ($this->copy_linked_contact($object, 'internal') < 0) { + $error++; + } + } + + if (!$error) { + // copy external contacts if same company + if (!empty($object->socid) && property_exists($this, 'fk_soc') && $this->fk_soc == $object->socid) { + if ($this->copy_linked_contact($object, 'external') < 0) { + $error++; + } + } + } + + unset($object->context['createfromclone']); + + // End + if (!$error) { + $this->db->commit(); + return $object; + } else { + $this->db->rollback(); + return -1; + } + } + + /** + * Load object in memory from the database + * + * @param int $id Id object + * @param string $ref Ref + * @return int <0 if KO, 0 if not found, >0 if OK + */ + public function fetch($id, $ref = null) + { + $result = $this->fetchCommon($id, $ref); + if ($result > 0 && !empty($this->table_element_line)) { + $this->fetchLines(); + } + return $result; + } + + /** + * Load object lines in memory from the database + * + * @return int <0 if KO, 0 if not found, >0 if OK + */ + public function fetchLines() + { + $this->lines = array(); + + $result = $this->fetchLinesCommon(); + return $result; + } + + + /** + * Load list of objects in memory from the database. + * + * @param string $sortorder Sort Order + * @param string $sortfield Sort field + * @param int $limit limit + * @param int $offset Offset + * @param array $filter Filter array. Example array('field'=>'valueforlike', 'customurl'=>...) + * @param string $filtermode Filter mode (AND or OR) + * @return array|int int <0 if KO, array of pages if OK + */ + public function fetchAll($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, array $filter = array(), $filtermode = 'AND') + { + global $conf; + + dol_syslog(__METHOD__, LOG_DEBUG); + + $records = array(); + + $sql = "SELECT "; + $sql .= $this->getFieldList('t'); + $sql .= " FROM ".$this->db->prefix().$this->table_element." as t"; + if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) { + $sql .= " WHERE t.entity IN (".getEntity($this->element).")"; + } else { + $sql .= " WHERE 1 = 1"; + } + // Manage filter + $sqlwhere = array(); + if (count($filter) > 0) { + foreach ($filter as $key => $value) { + if ($key == 't.rowid') { + $sqlwhere[] = $key." = ".((int) $value); + } elseif (in_array($this->fields[$key]['type'], array('date', 'datetime', 'timestamp'))) { + $sqlwhere[] = $key." = '".$this->db->idate($value)."'"; + } elseif ($key == 'customsql') { + $sqlwhere[] = $value; + } elseif (strpos($value, '%') === false) { + $sqlwhere[] = $key." IN (".$this->db->sanitize($this->db->escape($value)).")"; + } else { + $sqlwhere[] = $key." LIKE '%".$this->db->escape($value)."%'"; + } + } + } + if (count($sqlwhere) > 0) { + $sql .= " AND (".implode(" ".$filtermode." ", $sqlwhere).")"; + } + + if (!empty($sortfield)) { + $sql .= $this->db->order($sortfield, $sortorder); + } + if (!empty($limit)) { + $sql .= $this->db->plimit($limit, $offset); + } + + $resql = $this->db->query($sql); + if ($resql) { + $num = $this->db->num_rows($resql); + $i = 0; + while ($i < ($limit ? min($limit, $num) : $num)) { + $obj = $this->db->fetch_object($resql); + + $record = new self($this->db); + $record->setVarsFromFetchObj($obj); + + $records[$record->id] = $record; + + $i++; + } + $this->db->free($resql); + + return $records; + } else { + $this->errors[] = 'Error '.$this->db->lasterror(); + dol_syslog(__METHOD__.' '.join(',', $this->errors), LOG_ERR); + + return -1; + } + } + + /** + * Update object into database + * + * @param User $user User that modifies + * @param bool $notrigger false=launch triggers after, true=disable triggers + * @return int <0 if KO, >0 if OK + */ + public function update(User $user, $notrigger = false) + { + return $this->updateCommon($user, $notrigger); + } + + /** + * Delete object in database + * + * @param User $user User that deletes + * @param bool $notrigger false=launch triggers, true=disable triggers + * @return int <0 if KO, >0 if OK + */ + public function delete(User $user, $notrigger = false) + { + return $this->deleteCommon($user, $notrigger); + //return $this->deleteCommon($user, $notrigger, 1); + } + + /** + * Delete a line of object in database + * + * @param User $user User that delete + * @param int $idline Id of line to delete + * @param bool $notrigger false=launch triggers after, true=disable triggers + * @return int >0 if OK, <0 if KO + */ + public function deleteLine(User $user, $idline, $notrigger = false) + { + if ($this->status < 0) { + $this->error = 'ErrorDeleteLineNotAllowedByObjectStatus'; + return -2; + } + + return $this->deleteLineCommon($user, $idline, $notrigger); + } + + + /** + * Validate object + * + * @param User $user User making status change + * @param int $notrigger 1=Does not execute triggers, 0= execute triggers + * @return int <=0 if OK, 0=Nothing done, >0 if KO + */ + public function validate($user, $notrigger = 0) + { + global $conf, $langs; + + require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; + + $error = 0; + + // Protection + if ($this->status == self::STATUS_VALIDATED) { + dol_syslog(get_class($this)."::validate action abandonned: already validated", LOG_WARNING); + return 0; + } + + /*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->timespent->timespent->write)) + || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->timespent->timespent->timespent_advance->validate)))) + { + $this->error='NotEnoughPermissions'; + dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR); + return -1; + }*/ + + $now = dol_now(); + + $this->db->begin(); + + // Define new ref + if (!$error && (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life + $num = $this->getNextNumRef(); + } else { + $num = $this->ref; + } + $this->newref = $num; + + if (!empty($num)) { + // Validate + $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element; + $sql .= " SET ref = '".$this->db->escape($num)."',"; + $sql .= " status = ".self::STATUS_VALIDATED; + if (!empty($this->fields['date_validation'])) { + $sql .= ", date_validation = '".$this->db->idate($now)."'"; + } + if (!empty($this->fields['fk_user_valid'])) { + $sql .= ", fk_user_valid = ".((int) $user->id); + } + $sql .= " WHERE rowid = ".((int) $this->id); + + dol_syslog(get_class($this)."::validate()", LOG_DEBUG); + $resql = $this->db->query($sql); + if (!$resql) { + dol_print_error($this->db); + $this->error = $this->db->lasterror(); + $error++; + } + + if (!$error && !$notrigger) { + // Call trigger + $result = $this->call_trigger('timespent_VALIDATE', $user); + if ($result < 0) { + $error++; + } + // End call triggers + } + } + + if (!$error) { + $this->oldref = $this->ref; + + // Rename directory if dir was a temporary ref + if (preg_match('/^[\(]?PROV/i', $this->ref)) { + // Now we rename also files into index + $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filename = CONCAT('".$this->db->escape($this->newref)."', SUBSTR(filename, ".(strlen($this->ref) + 1).")), filepath = 'timespent/".$this->db->escape($this->newref)."'"; + $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'timespent/".$this->db->escape($this->ref)."' and entity = ".$conf->entity; + $resql = $this->db->query($sql); + if (!$resql) { + $error++; $this->error = $this->db->lasterror(); + } + + // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments + $oldref = dol_sanitizeFileName($this->ref); + $newref = dol_sanitizeFileName($num); + $dirsource = $conf->timespent->dir_output.'/timespent/'.$oldref; + $dirdest = $conf->timespent->dir_output.'/timespent/'.$newref; + if (!$error && file_exists($dirsource)) { + dol_syslog(get_class($this)."::validate() rename dir ".$dirsource." into ".$dirdest); + + if (@rename($dirsource, $dirdest)) { + dol_syslog("Rename ok"); + // Rename docs starting with $oldref with $newref + $listoffiles = dol_dir_list($conf->timespent->dir_output.'/timespent/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/')); + foreach ($listoffiles as $fileentry) { + $dirsource = $fileentry['name']; + $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource); + $dirsource = $fileentry['path'].'/'.$dirsource; + $dirdest = $fileentry['path'].'/'.$dirdest; + @rename($dirsource, $dirdest); + } + } + } + } + } + + // Set new ref and current status + if (!$error) { + $this->ref = $num; + $this->status = self::STATUS_VALIDATED; + } + + if (!$error) { + $this->db->commit(); + return 1; + } else { + $this->db->rollback(); + return -1; + } + } + + + /** + * Set draft status + * + * @param User $user Object user that modify + * @param int $notrigger 1=Does not execute triggers, 0=Execute triggers + * @return int <0 if KO, >0 if OK + */ + public function setDraft($user, $notrigger = 0) + { + // Protection + if ($this->status <= self::STATUS_DRAFT) { + return 0; + } + + /*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->timespent->write)) + || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->timespent->timespent_advance->validate)))) + { + $this->error='Permission denied'; + return -1; + }*/ + + return $this->setStatusCommon($user, self::STATUS_DRAFT, $notrigger, 'timespent_UNVALIDATE'); + } + + /** + * Set cancel status + * + * @param User $user Object user that modify + * @param int $notrigger 1=Does not execute triggers, 0=Execute triggers + * @return int <0 if KO, 0=Nothing done, >0 if OK + */ + public function cancel($user, $notrigger = 0) + { + // Protection + if ($this->status != self::STATUS_VALIDATED) { + return 0; + } + + /*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->timespent->write)) + || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->timespent->timespent_advance->validate)))) + { + $this->error='Permission denied'; + return -1; + }*/ + + return $this->setStatusCommon($user, self::STATUS_CANCELED, $notrigger, 'timespent_CANCEL'); + } + + /** + * Set back to validated status + * + * @param User $user Object user that modify + * @param int $notrigger 1=Does not execute triggers, 0=Execute triggers + * @return int <0 if KO, 0=Nothing done, >0 if OK + */ + public function reopen($user, $notrigger = 0) + { + // Protection + if ($this->status == self::STATUS_VALIDATED) { + return 0; + } + + /*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->timespent->write)) + || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->timespent->timespent_advance->validate)))) + { + $this->error='Permission denied'; + return -1; + }*/ + + return $this->setStatusCommon($user, self::STATUS_VALIDATED, $notrigger, 'timespent_REOPEN'); + } + + /** + * getTooltipContentArray + * @param array $params params to construct tooltip data + * @since v18 + * @return array + */ + public function getTooltipContentArray($params) + { + global $conf, $langs, $user; + + $datas = []; + + if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) { + return ['optimize' => $langs->trans("ShowTimeSpent")]; + } + $datas['picto'] = img_picto('', $this->picto).' '.$langs->trans("TimeSpent").''; + if (isset($this->status)) { + $datas['picto'] .= ' '.$this->getLibStatut(5); + } + $datas['ref'] .= '
'.$langs->trans('Ref').': '.$this->ref; + + return $datas; + } + + /** + * Return a link to the object card (with optionaly the picto) + * + * @param int $withpicto Include picto in link (0=No picto, 1=Include picto into link, 2=Only picto) + * @param string $option On what the link point to ('nolink', ...) + * @param int $notooltip 1=Disable tooltip + * @param string $morecss Add more css on link + * @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 = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = -1) + { + global $conf, $langs, $hookmanager; + + if (!empty($conf->dol_no_mouse_hover)) { + $notooltip = 1; // Force disable tooltips + } + + $result = ''; + $params = [ + 'id' => $this->id, + 'objecttype' => $this->element, + 'option' => $option, + ]; + $classfortooltip = 'classfortooltip'; + $dataparams = ''; + if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) { + $classfortooltip = 'classforajaxtooltip'; + $dataparams = " data-params='".json_encode($params)."'"; + // $label = $langs->trans('Loading'); + } + $label = implode($this->getTooltipContentArray($params)); + + $url = dol_buildpath('/timespent/timespent_card.php', 1).'?id='.$this->id; + + if ($option != 'nolink') { + // 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 ($url && $add_save_lastsearch_values) { + $url .= '&save_lastsearch_values=1'; + } + } + + $linkclose = ''; + if (empty($notooltip)) { + if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) { + $label = $langs->trans("ShowTimeSpent"); + $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"'; + } + $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"'; + $linkclose .= $dataparams.' class="'.$classfortooltip.($morecss ? ' '.$morecss : '').'"'; + } else { + $linkclose = ($morecss ? ' class="'.$morecss.'"' : ''); + } + + if ($option == 'nolink' || empty($url)) { + $linkstart = ''; + if ($option == 'nolink' || empty($url)) { + $linkend = ''; + } else { + $linkend = ''; + } + + $result .= $linkstart; + + if (empty($this->showphoto_on_popup)) { + if ($withpicto) { + $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : $dataparams.' class="'.(($withpicto != 2) ? 'paddingright ' : '').$classfortooltip.'"'), 0, 0, $notooltip ? 0 : 1); + } + } else { + if ($withpicto) { + require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; + + list($class, $module) = explode('@', $this->picto); + $upload_dir = $conf->$module->multidir_output[$conf->entity]."/$class/".dol_sanitizeFileName($this->ref); + $filearray = dol_dir_list($upload_dir, "files"); + $filename = $filearray[0]['name']; + if (!empty($filename)) { + $pospoint = strpos($filearray[0]['name'], '.'); + + $pathtophoto = $class.'/'.$this->ref.'/thumbs/'.substr($filename, 0, $pospoint).'_mini'.substr($filename, $pospoint); + if (empty($conf->global->{strtoupper($module.'_'.$class).'_FORMATLISTPHOTOSASUSERS'})) { + $result .= '
No photo
'; + } else { + $result .= '
No photo
'; + } + + $result .= ''; + } else { + $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), ($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->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : ''); + + global $action, $hookmanager; + $hookmanager->initHooks(array($this->element.'dao')); + $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 a thumb for kanban views + * + * @param string $option Where point the link (0=> main card, 1,2 => shipment, 'nolink'=>No link) + * @param array $arraydata Array of data + * @return string HTML Code for Kanban thumb. + */ + public function getKanbanView($option = '', $arraydata = null) + { + global $conf, $langs; + $return = '
'; + $return .= '
'; + $return .= ''; + $return .= img_picto('', $this->picto); + $return .= ''; + $return .= '
'; + $return .= ''.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).''; + if (property_exists($this, 'label')) { + $return .= '
'.$this->label.'
'; + } + if (property_exists($this, 'amount')) { + $return .= '
'; + $return .= ''.price($this->amount, 0, $langs, 1, -1, -1, $conf->currency).''; + } + if (method_exists($this, 'getLibStatut')) { + $return .= '
'.$this->getLibStatut(5).'
'; + } + $return .= '
'; + $return .= '
'; + $return .= '
'; + + return $return; + } + + /** + * Return the label of the 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 of status + */ + public function getLabelStatus($mode = 0) + { + return $this->LibStatut($this->status, $mode); + } + + /** + * Return the label of the 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 of status + */ + public function getLibStatut($mode = 0) + { + return $this->LibStatut($this->status, $mode); + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return the 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 of status + */ + public function LibStatut($status, $mode = 0) + { + // phpcs:enable + if (empty($this->labelStatus) || empty($this->labelStatusShort)) { + global $langs; + //$langs->load("timespent@timespent"); + $this->labelStatus[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv('Draft'); + $this->labelStatus[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv('Enabled'); + $this->labelStatus[self::STATUS_CANCELED] = $langs->transnoentitiesnoconv('Disabled'); + $this->labelStatusShort[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv('Draft'); + $this->labelStatusShort[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv('Enabled'); + $this->labelStatusShort[self::STATUS_CANCELED] = $langs->transnoentitiesnoconv('Disabled'); + } + + $statusType = 'status'.$status; + //if ($status == self::STATUS_VALIDATED) $statusType = 'status1'; + if ($status == self::STATUS_CANCELED) { + $statusType = 'status6'; + } + + return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusType, $mode); + } + + /** + * Load the info information in the object + * + * @param int $id Id of object + * @return void + */ + public function info($id) + { + $sql = "SELECT rowid,"; + $sql .= " date_creation as datec, tms as datem,"; + $sql .= " fk_user_creat, fk_user_modif"; + $sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as t"; + $sql .= " WHERE t.rowid = ".((int) $id); + + $result = $this->db->query($sql); + if ($result) { + if ($this->db->num_rows($result)) { + $obj = $this->db->fetch_object($result); + + $this->id = $obj->rowid; + + $this->user_creation_id = $obj->fk_user_creat; + $this->user_modification_id = $obj->fk_user_modif; + if (!empty($obj->fk_user_valid)) { + $this->user_validation_id = $obj->fk_user_valid; + } + $this->date_creation = $this->db->jdate($obj->datec); + $this->date_modification = empty($obj->datem) ? '' : $this->db->jdate($obj->datem); + if (!empty($obj->datev)) { + $this->date_validation = empty($obj->datev) ? '' : $this->db->jdate($obj->datev); + } + } + + $this->db->free($result); + } else { + dol_print_error($this->db); + } + } + + /** + * Initialise object with example values + * Id must be 0 if object instance is a specimen + * + * @return void + */ + public function initAsSpecimen() + { + // Set here init that are not commonf fields + // $this->property1 = ... + // $this->property2 = ... + + $this->initAsSpecimenCommon(); + } + + /** + * Create an array of lines + * + * @return array|int array of lines if OK, <0 if KO + */ + public function getLinesArray() + { + $this->lines = array(); + + $objectline = new TimeSpentLine($this->db); + $result = $objectline->fetchAll('ASC', 'position', 0, 0, array('customsql'=>'fk_timespent = '.((int) $this->id))); + + if (is_numeric($result)) { + $this->error = $objectline->error; + $this->errors = $objectline->errors; + return $result; + } else { + $this->lines = $result; + return $this->lines; + } + } + + /** + * Returns the reference to the following non used object depending on the active numbering module. + * + * @return string Object free reference + */ + public function getNextNumRef() + { + global $langs, $conf; + $langs->load("timespent@timespent"); + + if (empty($conf->global->TIMESPENT_timespent_ADDON)) { + $conf->global->TIMESPENT_timespent_ADDON = 'mod_timespent_standard'; + } + + if (!empty($conf->global->TIMESPENT_timespent_ADDON)) { + $mybool = false; + + $file = $conf->global->TIMESPENT_timespent_ADDON.".php"; + $classname = $conf->global->TIMESPENT_timespent_ADDON; + + // Include file with class + $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']); + foreach ($dirmodels as $reldir) { + $dir = dol_buildpath($reldir."core/modules/timespent/"); + + // Load file with numbering class (if found) + $mybool |= @include_once $dir.$file; + } + + if ($mybool === false) { + dol_print_error('', "Failed to include file ".$file); + return ''; + } + + if (class_exists($classname)) { + $obj = new $classname(); + $numref = $obj->getNextValue($this); + + if ($numref != '' && $numref != '-1') { + return $numref; + } else { + $this->error = $obj->error; + //dol_print_error($this->db,get_class($this)."::getNextNumRef ".$obj->error); + return ""; + } + } else { + print $langs->trans("Error")." ".$langs->trans("ClassNotFound").' '.$classname; + return ""; + } + } else { + print $langs->trans("ErrorNumberingModuleNotSetup", $this->element); + return ""; + } + } + + /** + * Create a document onto disk according to template module. + * + * @param string $modele Force template to use ('' to not force) + * @param Translate $outputlangs objet lang a utiliser pour traduction + * @param int $hidedetails Hide details of lines + * @param int $hidedesc Hide description + * @param int $hideref Hide ref + * @param null|array $moreparams Array to provide more information + * @return int 0 if KO, 1 if OK + */ + public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null) + { + global $conf, $langs; + + $result = 0; + $includedocgeneration = 0; + + $langs->load("timespent@timespent"); + + if (!dol_strlen($modele)) { + $modele = 'standard_timespent'; + + if (!empty($this->model_pdf)) { + $modele = $this->model_pdf; + } elseif (!empty($conf->global->timespent_ADDON_PDF)) { + $modele = $conf->global->timespent_ADDON_PDF; + } + } + + $modelpath = "core/modules/timespent/doc/"; + + if ($includedocgeneration && !empty($modele)) { + $result = $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams); + } + + return $result; + } + + /** + * Action executed by scheduler + * CAN BE A CRON TASK. In such a case, parameters come from the schedule job setup field 'Parameters' + * Use public function doScheduledJob($param1, $param2, ...) to get parameters + * + * @return int 0 if OK, <>0 if KO (this function is used also by cron so only 0 is OK) + */ + public function doScheduledJob() + { + //global $conf, $langs; + + //$conf->global->SYSLOG_FILE = 'DOL_DATA_ROOT/dolibarr_mydedicatedlofile.log'; + + $error = 0; + $this->output = ''; + $this->error = ''; + + dol_syslog(__METHOD__, LOG_DEBUG); + + $now = dol_now(); + + $this->db->begin(); + + // ... + + $this->db->commit(); + + return $error; + } +} diff --git a/htdocs/core/lib/project.lib.php b/htdocs/core/lib/project.lib.php index 8aa8b6d93b7..71c0cd7c0c6 100644 --- a/htdocs/core/lib/project.lib.php +++ b/htdocs/core/lib/project.lib.php @@ -4,6 +4,7 @@ * Copyright (C) 2011 Juanjo Menent * Copyright (C) 2018-2021 Frédéric France * Copyright (C) 2022 Charlene Benke + * Copyright (C) 2023 Gauthier VERDOL * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -100,8 +101,9 @@ function project_prepare_head(Project $project, $moreparam = '') $sql = "SELECT t.rowid"; //$sql .= " FROM ".MAIN_DB_PREFIX."projet_task_time as t, ".MAIN_DB_PREFIX."projet_task as pt, ".MAIN_DB_PREFIX."user as u"; //$sql .= " WHERE t.fk_user = u.rowid AND t.fk_task = pt.rowid"; - $sql .= " FROM ".MAIN_DB_PREFIX."projet_task_time as t, ".MAIN_DB_PREFIX."projet_task as pt"; - $sql .= " WHERE t.fk_task = pt.rowid"; + $sql .= " FROM ".MAIN_DB_PREFIX."element_time as t, ".MAIN_DB_PREFIX."projet_task as pt"; + $sql .= " WHERE t.fk_element = pt.rowid"; + $sql .= " AND t.elementtype = 'task'"; $sql .= " AND pt.fk_projet =".((int) $project->id); $resql = $db->query($sql); if ($resql) { @@ -372,8 +374,8 @@ function task_prepare_head($object) $sql = "SELECT t.rowid"; //$sql .= " FROM ".MAIN_DB_PREFIX."projet_task_time as t, ".MAIN_DB_PREFIX."projet_task as pt, ".MAIN_DB_PREFIX."user as u"; //$sql .= " WHERE t.fk_user = u.rowid AND t.fk_task = pt.rowid"; - $sql .= " FROM ".MAIN_DB_PREFIX."projet_task_time as t"; - $sql .= " WHERE t.fk_task = ".((int) $object->id); + $sql .= " FROM ".MAIN_DB_PREFIX."element_time as t"; + $sql .= " WHERE t.elementtype='task' AND t.fk_element = ".((int) $object->id); $resql = $db->query($sql); if ($resql) { $obj = $db->fetch_object($resql); diff --git a/htdocs/core/modules/modProjet.class.php b/htdocs/core/modules/modProjet.class.php index 8bc8e03adb2..be6c641f852 100644 --- a/htdocs/core/modules/modProjet.class.php +++ b/htdocs/core/modules/modProjet.class.php @@ -5,7 +5,8 @@ * Copyright (C) 2004 Benoit Mortier * Copyright (C) 2005-2012 Regis Houssin * Copyright (C) 2013 Florian Henry - * Copyright (C) 2014 Charles-Fr BENKE + * Copyright (C) 2014 Charles-Fr BENKE + * Copyright (C) 2023 Gauthier VERDOL * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -230,7 +231,7 @@ class modProjet extends DolibarrModules 'p.usage_opportunity'=>'Boolean', 'p.usage_task'=>'Boolean', 'p.usage_bill_time'=>'Boolean', 'p.datec'=>"Date", 'p.dateo'=>"Date", 'p.datee'=>"Date", 'p.fk_statut'=>'Status', 'cls.code'=>"Text", 'p.opp_percent'=>'Numeric', 'p.opp_amount'=>'Numeric', 'p.description'=>"Text", 'p.entity'=>'Numeric', 'p.budget_amount'=>'Numeric', 'pt.rowid'=>'Numeric', 'pt.ref'=>'Text', 'pt.label'=>'Text', 'pt.dateo'=>"Date", 'pt.datee'=>"Date", 'pt.duration_effective'=>"Duree", 'pt.planned_workload'=>"Numeric", 'pt.progress'=>"Numeric", 'pt.description'=>"Text", - 'ptt.rowid'=>'Numeric', 'ptt.task_date'=>'Date', 'ptt.task_duration'=>"Duree", 'ptt.fk_user'=>"FormSelect:select_dolusers", 'ptt.note'=>"Text" + 'ptt.rowid'=>'Numeric', 'ptt.element_date'=>'Date', 'ptt.element_duration'=>"Duree", 'ptt.fk_user'=>"FormSelect:select_dolusers", 'ptt.note'=>"Text" ); $this->export_entities_array[$r] = array( 's.rowid'=>"company", 's.nom'=>'company', 's.address'=>'company', 's.zip'=>'company', 's.town'=>'company', 's.fk_pays'=>'company', @@ -272,8 +273,8 @@ class modProjet extends DolibarrModules $keyforaliasextra = 'extra2'; include DOL_DOCUMENT_ROOT.'/core/extrafieldsinexport.inc.php'; // End add extra fields - $this->export_fields_array[$r] = array_merge($this->export_fields_array[$r], array('ptt.rowid'=>'IdTaskTime', 'ptt.task_date'=>'TaskTimeDate', 'ptt.task_duration'=>"TimesSpent", 'ptt.fk_user'=>"TaskTimeUser", 'ptt.note'=>"TaskTimeNote")); - $this->export_entities_array[$r] = array_merge($this->export_entities_array[$r], array('ptt.rowid'=>'task_time', 'ptt.task_date'=>'task_time', 'ptt.task_duration'=>"task_time", 'ptt.fk_user'=>"task_time", 'ptt.note'=>"task_time")); + $this->export_fields_array[$r] = array_merge($this->export_fields_array[$r], array('ptt.rowid'=>'IdTaskTime', 'ptt.element_date'=>'TaskTimeDate', 'ptt.element_duration'=>"TimesSpent", 'ptt.fk_user'=>"TaskTimeUser", 'ptt.note'=>"TaskTimeNote")); + $this->export_entities_array[$r] = array_merge($this->export_entities_array[$r], array('ptt.rowid'=>'task_time', 'ptt.element_date'=>'task_time', 'ptt.element_duration'=>"task_time", 'ptt.fk_user'=>"task_time", 'ptt.note'=>"task_time")); if (empty($conf->global->PROJECT_HIDE_TASKS)) { $this->export_fields_array[$r] = array_merge($this->export_fields_array[$r], array('f.ref'=>"Billed")); $this->export_entities_array[$r] = array_merge($this->export_entities_array[$r], array('f.ref'=>"task_time")); @@ -284,7 +285,7 @@ class modProjet extends DolibarrModules $this->export_sql_end[$r] .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_lead_status as cls ON p.fk_opp_status = cls.rowid'; $this->export_sql_end[$r] .= ' LEFT JOIN '.MAIN_DB_PREFIX."projet_task as pt ON p.rowid = pt.fk_projet"; $this->export_sql_end[$r] .= ' LEFT JOIN '.MAIN_DB_PREFIX.'projet_task_extrafields as extra2 ON pt.rowid = extra2.fk_object'; - $this->export_sql_end[$r] .= ' LEFT JOIN '.MAIN_DB_PREFIX."projet_task_time as ptt ON pt.rowid = ptt.fk_task"; + $this->export_sql_end[$r] .= ' LEFT JOIN '.MAIN_DB_PREFIX."element_time as ptt ON (pt.rowid = ptt.fk_element AND ptt.elementtype = 'task')"; $this->export_sql_end[$r] .= ' LEFT JOIN '.MAIN_DB_PREFIX.'societe as s ON p.fk_soc = s.rowid'; if (empty($conf->global->PROJECT_HIDE_TASKS)) { $this->export_sql_end[$r] .= ' LEFT JOIN '.MAIN_DB_PREFIX.'facture as f ON ptt.invoice_id = f.rowid'; diff --git a/htdocs/core/modules/project/doc/doc_generic_project_odt.modules.php b/htdocs/core/modules/project/doc/doc_generic_project_odt.modules.php index 635a627d0a1..16a197f42b9 100644 --- a/htdocs/core/modules/project/doc/doc_generic_project_odt.modules.php +++ b/htdocs/core/modules/project/doc/doc_generic_project_odt.modules.php @@ -3,7 +3,8 @@ * Copyright (C) 2012 Juanjo Menent * Copyright (C) 2013 Florian Henry * Copyright (C) 2016 Charlie Benke - * Copyright (C) 2018 Frédéric France + * Copyright (C) 2018 Frédéric France + * Copyright (C) 2023 Gauthier VERDOL * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -749,13 +750,14 @@ class doc_generic_project_odt extends ModelePDFProjects } //Time ressources - $sql = "SELECT t.rowid, t.task_date, t.task_duration, t.fk_user, t.note"; + $sql = "SELECT t.rowid, t.element_date as task_date, t.element_duration as task_duration, t.fk_user, t.note"; $sql .= ", u.lastname, u.firstname, t.thm"; - $sql .= " FROM ".MAIN_DB_PREFIX."projet_task_time as t"; + $sql .= " FROM ".MAIN_DB_PREFIX."element_time as t"; $sql .= " , ".MAIN_DB_PREFIX."user as u"; - $sql .= " WHERE t.fk_task =".((int) $task->id); + $sql .= " WHERE t.fk_element =".((int) $task->id); + $sql .= " AND t.elementtype = 'task'"; $sql .= " AND t.fk_user = u.rowid"; - $sql .= " ORDER BY t.task_date DESC"; + $sql .= " ORDER BY t.element_date DESC"; $resql = $this->db->query($sql); if ($resql) { diff --git a/htdocs/core/modules/project/task/doc/doc_generic_task_odt.modules.php b/htdocs/core/modules/project/task/doc/doc_generic_task_odt.modules.php index 49522a01d33..983d6f9b97a 100644 --- a/htdocs/core/modules/project/task/doc/doc_generic_task_odt.modules.php +++ b/htdocs/core/modules/project/task/doc/doc_generic_task_odt.modules.php @@ -3,7 +3,8 @@ * Copyright (C) 2012 Juanjo Menent * Copyright (C) 2013 Florian Henry * Copyright (C) 2016 Charlie Benke - * Copyright (C) 2018 Frédéric France + * Copyright (C) 2018 Frédéric France + * Copyright (C) 2023 Gauthier VERDOL * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -662,13 +663,14 @@ class doc_generic_task_odt extends ModelePDFTask } // Time ressources - $sql = "SELECT t.rowid, t.task_date, t.task_duration, t.fk_user, t.note"; + $sql = "SELECT t.rowid, t.element_date as task_date, t.element_duration as task_duration, t.fk_user, t.note"; $sql .= ", u.lastname, u.firstname"; - $sql .= " FROM ".MAIN_DB_PREFIX."projet_task_time as t"; + $sql .= " FROM ".MAIN_DB_PREFIX."element_time as t"; $sql .= " , ".MAIN_DB_PREFIX."user as u"; - $sql .= " WHERE t.fk_task =".((int) $object->id); + $sql .= " WHERE t.fk_element =".((int) $object->id); + $sql .= " AND t.elementtype = 'task'"; $sql .= " AND t.fk_user = u.rowid"; - $sql .= " ORDER BY t.task_date DESC"; + $sql .= " ORDER BY t.element_date DESC"; $resql = $this->db->query($sql); if ($resql) { diff --git a/htdocs/install/mysql/migration/repair.sql b/htdocs/install/mysql/migration/repair.sql index 26469449a0b..25be39f2c87 100644 --- a/htdocs/install/mysql/migration/repair.sql +++ b/htdocs/install/mysql/migration/repair.sql @@ -315,7 +315,7 @@ UPDATE llx_actioncomm set fk_user_action = fk_user_done where fk_user_done > 0 a UPDATE llx_actioncomm set fk_user_action = fk_user_author where fk_user_author > 0 and (fk_user_action is null or fk_user_action = 0); -UPDATE llx_projet_task_time set task_datehour = task_date where task_datehour IS NULL and task_date IS NOT NULL; +UPDATE llx_element_time set element_datehour = element_date where element_datehour IS NULL and element_date IS NOT NULL; UPDATE llx_projet set fk_opp_status = NULL where fk_opp_status = -1; UPDATE llx_projet set fk_opp_status = (SELECT rowid FROM llx_c_lead_status WHERE code='PROSP') where fk_opp_status IS NULL and opp_amount > 0; @@ -389,8 +389,8 @@ update llx_expedition set date_valid = date_creation where fk_statut = 1 and dat update llx_expedition set date_valid = NOW() where fk_statut = 1 and date_valid IS NULL; -- Detect bad consistency between duraction_effective of a task and sum of time of tasks --- select pt.rowid, pt.duration_effective, SUM(ptt.task_duration) as y from llx_projet_task as pt, llx_projet_task_time as ptt where ptt.fk_task = pt.rowid group by pt.rowid, pt.duration_effective having pt.duration_effective <> y; -update llx_projet_task as pt set pt.duration_effective = (select SUM(ptt.task_duration) as y from llx_projet_task_time as ptt where ptt.fk_task = pt.rowid) where pt.duration_effective <> (select SUM(ptt.task_duration) as y from llx_projet_task_time as ptt where ptt.fk_task = pt.rowid); +-- select pt.rowid, pt.duration_effective, SUM(ptt.element_duration) as y from llx_projet_task as pt, llx_element_time as ptt where ptt.fk_element = pt.rowid and ptt.elementtype = 'task' group by pt.rowid, pt.duration_effective having pt.duration_effective <> y; +update llx_projet_task as pt set pt.duration_effective = (select SUM(ptt.element_duration) as y from llx_element_time as ptt where ptt.fk_element = pt.rowid and ptt.elementtype = 'task') where pt.duration_effective <> (select SUM(ptt.element_duration) as y from llx_element_time as ptt where ptt.fk_element = pt.rowid and ptt.elementtype = 'task'); -- Remove duplicate of shipment mode (keep the one with tracking defined) @@ -422,7 +422,7 @@ UPDATE llx_facture_fourn_det SET fk_code_ventilation = 0 WHERE fk_code_ventilati UPDATE llx_expensereport_det SET fk_code_ventilation = 0 WHERE fk_code_ventilation > 0 AND fk_code_ventilation NOT IN (select rowid FROM llx_accounting_account); --- VMYSQL4.1 update llx_projet_task_time set task_datehour = task_date where task_datehour < task_date or task_datehour > DATE_ADD(task_date, interval 1 day); +-- VMYSQL4.1 update llx_element_time set element_datehour = element_date where element_datehour < element_date or element_datehour > DATE_ADD(element_date, interval 1 day); -- Clean product prices @@ -487,7 +487,7 @@ UPDATE llx_chargesociales SET date_creation = tms WHERE date_creation IS NULL; -- Backport a change of value into the hourly rate. --- update llx_projet_task_time as ptt set ptt.thm = (SELECT thm from llx_user as u where ptt.fk_user = u.rowid) where (ptt.thm is null) +-- update llx_element_time as ptt set ptt.thm = (SELECT thm from llx_user as u where ptt.fk_user = u.rowid) where (ptt.thm is null) -- select * from llx_facturedet as fd, llx_product as p where fd.fk_product = p.rowid AND fd.product_type != p.fk_product_type; diff --git a/htdocs/install/pgsql/functions/functions.sql b/htdocs/install/pgsql/functions/functions.sql index 35bc4e05c11..918d17385eb 100644 --- a/htdocs/install/pgsql/functions/functions.sql +++ b/htdocs/install/pgsql/functions/functions.sql @@ -1,6 +1,7 @@ -- ============================================================================ -- Copyright (C) 2004 Benoit Mortier -- Copyright (C) 2010 Laurent Destailleur +-- Copyright (C) 2023 Gauthier VERDOL -- -- This program is free software; you can redistribute it and/or modify -- it under the terms of the GNU General Public License as published by @@ -150,7 +151,7 @@ CREATE TRIGGER update_customer_modtime BEFORE UPDATE ON llx_projet FOR EACH ROW CREATE TRIGGER update_customer_modtime BEFORE UPDATE ON llx_projet_extrafields FOR EACH ROW EXECUTE PROCEDURE update_modified_column_tms(); CREATE TRIGGER update_customer_modtime BEFORE UPDATE ON llx_projet_task FOR EACH ROW EXECUTE PROCEDURE update_modified_column_tms(); CREATE TRIGGER update_customer_modtime BEFORE UPDATE ON llx_projet_task_extrafields FOR EACH ROW EXECUTE PROCEDURE update_modified_column_tms(); -CREATE TRIGGER update_customer_modtime BEFORE UPDATE ON llx_projet_task_time FOR EACH ROW EXECUTE PROCEDURE update_modified_column_tms(); +CREATE TRIGGER update_customer_modtime BEFORE UPDATE ON llx_element_time FOR EACH ROW EXECUTE PROCEDURE update_modified_column_tms(); CREATE TRIGGER update_customer_modtime BEFORE UPDATE ON llx_propal FOR EACH ROW EXECUTE PROCEDURE update_modified_column_tms(); CREATE TRIGGER update_customer_modtime BEFORE UPDATE ON llx_propal_extrafields FOR EACH ROW EXECUTE PROCEDURE update_modified_column_tms(); CREATE TRIGGER update_customer_modtime BEFORE UPDATE ON llx_propal_merge_pdf_product FOR EACH ROW EXECUTE PROCEDURE update_modified_column_tms(); diff --git a/htdocs/install/repair.php b/htdocs/install/repair.php index c40f4a69e89..70be057b7b8 100644 --- a/htdocs/install/repair.php +++ b/htdocs/install/repair.php @@ -4,6 +4,7 @@ * Copyright (C) 2005-2012 Regis Houssin * Copyright (C) 2015 Raphaël Doursenaud * Copyright (C) 2021 Frédéric France + * Copyright (C) 2023 Gauthier VERDOL * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -1030,7 +1031,7 @@ if ($ok && GETPOST('set_empty_time_spent_amount', 'alpha')) { print '
*** Set value of time spent without amount'; $sql = "SELECT COUNT(ptt.rowid) as nb, u.rowid as user_id, u.login, u.thm as user_thm"; - $sql .= " FROM ".MAIN_DB_PREFIX."projet_task_time as ptt, ".MAIN_DB_PREFIX."user as u"; + $sql .= " FROM ".MAIN_DB_PREFIX."element_time as ptt, ".MAIN_DB_PREFIX."user as u"; $sql .= " WHERE ptt.fk_user = u.rowid"; $sql .= " AND ptt.thm IS NULL and u.thm > 0"; $sql .= " GROUP BY u.rowid, u.login, u.thm"; @@ -1048,7 +1049,7 @@ if ($ok && GETPOST('set_empty_time_spent_amount', 'alpha')) { $db->begin(); if (GETPOST('set_empty_time_spent_amount') == 'confirmed') { - $sql2 = "UPDATE ".MAIN_DB_PREFIX."projet_task_time"; + $sql2 = "UPDATE ".MAIN_DB_PREFIX."element_time"; $sql2 .= " SET thm = ".$obj->user_thm." WHERE thm IS NULL AND fk_user = ".((int) $obj->user_id); $resql2 = $db->query($sql2); if (!$resql2) { diff --git a/htdocs/install/upgrade2.php b/htdocs/install/upgrade2.php index 7f76261bcea..65629284af3 100644 --- a/htdocs/install/upgrade2.php +++ b/htdocs/install/upgrade2.php @@ -4,6 +4,7 @@ * Copyright (C) 2005-2011 Regis Houssin * Copyright (C) 2010 Juanjo Menent * Copyright (C) 2015-2016 Raphaël Doursenaud + * Copyright (C) 2023 Gauthier VERDOL * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -339,7 +340,7 @@ if (!GETPOST('action', 'aZ09') || preg_match('/upgrade/i', GETPOST('action', 'aZ $afterversionarray = explode('.', '2.8.9'); $beforeversionarray = explode('.', '2.9.9'); if (versioncompare($versiontoarray, $afterversionarray) >= 0 && versioncompare($versiontoarray, $beforeversionarray) <= 0) { - migrate_project_task_time($db, $langs, $conf); + migrate_element_time($db, $langs, $conf); migrate_customerorder_shipping($db, $langs, $conf); @@ -2901,9 +2902,9 @@ function migrate_relationship_tables($db, $langs, $conf, $table, $fk_source, $so * @param Conf $conf Object conf * @return void */ -function migrate_project_task_time($db, $langs, $conf) +function migrate_element_time($db, $langs, $conf) { - dolibarr_install_syslog("upgrade2::migrate_project_task_time"); + dolibarr_install_syslog("upgrade2::migrate_element_time"); print ''; @@ -2914,8 +2915,8 @@ function migrate_project_task_time($db, $langs, $conf) $db->begin(); - $sql = "SELECT rowid, fk_task, task_duration"; - $sql .= " FROM ".MAIN_DB_PREFIX."projet_task_time"; + $sql = "SELECT rowid, fk_element, element_duration"; + $sql .= " FROM ".MAIN_DB_PREFIX."element_time"; $resql = $db->query($sql); if ($resql) { $i = 0; @@ -2928,16 +2929,16 @@ function migrate_project_task_time($db, $langs, $conf) while ($i < $num) { $obj = $db->fetch_object($resql); - if ($obj->task_duration > 0) { + if ($obj->element_duration > 0) { // convert to second // only for int time and float time ex: 1,75 for 1h45 - list($hour, $min) = explode('.', $obj->task_duration); + list($hour, $min) = explode('.', $obj->element_duration); $hour = $hour * 60 * 60; $min = ($min / 100) * 60 * 60; $newtime = $hour + $min; - $sql2 = "UPDATE ".MAIN_DB_PREFIX."projet_task_time SET"; - $sql2 .= " task_duration = ".((int) $newtime); + $sql2 = "UPDATE ".MAIN_DB_PREFIX."element_time SET"; + $sql2 .= " element_duration = ".((int) $newtime); $sql2 .= " WHERE rowid = ".((int) $obj->rowid); $resql2 = $db->query($sql2); @@ -2947,16 +2948,16 @@ function migrate_project_task_time($db, $langs, $conf) } print ". "; $oldtime++; - if (!empty($totaltime[$obj->fk_task])) { - $totaltime[$obj->fk_task] += $newtime; + if (!empty($totaltime[$obj->fk_element])) { + $totaltime[$obj->fk_element] += $newtime; } else { - $totaltime[$obj->fk_task] = $newtime; + $totaltime[$obj->fk_element] = $newtime; } } else { - if (!empty($totaltime[$obj->fk_task])) { - $totaltime[$obj->fk_task] += $obj->task_duration; + if (!empty($totaltime[$obj->fk_element])) { + $totaltime[$obj->fk_element] += $obj->element_duration; } else { - $totaltime[$obj->fk_task] = $obj->task_duration; + $totaltime[$obj->fk_element] = $obj->element_duration; } } diff --git a/htdocs/projet/activity/index.php b/htdocs/projet/activity/index.php index bbd9442f876..8f69a8afada 100644 --- a/htdocs/projet/activity/index.php +++ b/htdocs/projet/activity/index.php @@ -3,6 +3,7 @@ * Copyright (C) 2006-2015 Laurent Destailleur * Copyright (C) 2010 Regis Houssin * Copyright (C) 2019 Nicolas ZABOURI + * Copyright (C) 2023 Gauthier VERDOL * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -116,15 +117,16 @@ print ''.$langs->trans('ActivityOnProjectToday').''; print ''.$langs->trans("Time").''; print "\n"; -$sql = "SELECT p.rowid, p.ref, p.title, p.public, SUM(tt.task_duration) as nb"; +$sql = "SELECT p.rowid, p.ref, p.title, p.public, SUM(tt.element_duration) as nb"; $sql .= " FROM ".MAIN_DB_PREFIX."projet as p"; $sql .= ", ".MAIN_DB_PREFIX."projet_task as t"; -$sql .= ", ".MAIN_DB_PREFIX."projet_task_time as tt"; +$sql .= ", ".MAIN_DB_PREFIX."element_time as tt"; $sql .= " WHERE t.fk_projet = p.rowid"; $sql .= " AND p.entity = ".((int) $conf->entity); -$sql .= " AND tt.fk_task = t.rowid"; +$sql .= " AND tt.fk_element = t.rowid"; +$sql .= " AND tt.elementtype = 'task'"; $sql .= " AND tt.fk_user = ".((int) $user->id); -$sql .= " AND task_date BETWEEN '".$db->idate(dol_mktime(0, 0, 0, $month, $day, $year))."' AND '".$db->idate(dol_mktime(23, 59, 59, $month, $day, $year))."'"; +$sql .= " AND element_date BETWEEN '".$db->idate(dol_mktime(0, 0, 0, $month, $day, $year))."' AND '".$db->idate(dol_mktime(23, 59, 59, $month, $day, $year))."'"; $sql .= " AND p.rowid in (".$db->sanitize($projectsListId).")"; $sql .= " GROUP BY p.rowid, p.ref, p.title, p.public"; @@ -169,15 +171,16 @@ print ''.$langs->trans('ActivityOnProjectYesterday').''; print ''.$langs->trans("Time").''; print "\n"; -$sql = "SELECT p.rowid, p.ref, p.title, p.public, SUM(tt.task_duration) as nb"; +$sql = "SELECT p.rowid, p.ref, p.title, p.public, SUM(tt.element_duration) as nb"; $sql .= " FROM ".MAIN_DB_PREFIX."projet as p"; $sql .= ", ".MAIN_DB_PREFIX."projet_task as t"; -$sql .= ", ".MAIN_DB_PREFIX."projet_task_time as tt"; +$sql .= ", ".MAIN_DB_PREFIX."element_time as tt"; $sql .= " WHERE t.fk_projet = p.rowid"; $sql .= " AND p.entity = ".((int) $conf->entity); -$sql .= " AND tt.fk_task = t.rowid"; +$sql .= " AND tt.fk_element = t.rowid"; +$sql .= " AND tt.elementtype = 'task'"; $sql .= " AND tt.fk_user = ".((int) $user->id); -$sql .= " AND task_date BETWEEN '".$db->idate(dol_time_plus_duree(dol_mktime(0, 0, 0, $month, $day, $year), -1, 'd'))."' AND '".$db->idate(dol_time_plus_duree(dol_mktime(23, 59, 59, $month, $day, $year), -1, 'd'))."'"; +$sql .= " AND element_date BETWEEN '".$db->idate(dol_time_plus_duree(dol_mktime(0, 0, 0, $month, $day, $year), -1, 'd'))."' AND '".$db->idate(dol_time_plus_duree(dol_mktime(23, 59, 59, $month, $day, $year), -1, 'd'))."'"; $sql .= " AND p.rowid in (".$db->sanitize($projectsListId).")"; $sql .= " GROUP BY p.rowid, p.ref, p.title, p.public"; @@ -281,15 +284,16 @@ if (!empty($conf->global->PROJECT_TASK_TIME_MONTH)) { print ''.$langs->trans("Time").''; print "\n"; - $sql = "SELECT p.rowid, p.ref, p.title, p.public, SUM(tt.task_duration) as nb"; + $sql = "SELECT p.rowid, p.ref, p.title, p.public, SUM(tt.element_duration) as nb"; $sql .= " FROM ".MAIN_DB_PREFIX."projet as p"; $sql .= ", ".MAIN_DB_PREFIX."projet_task as t"; - $sql .= ", ".MAIN_DB_PREFIX."projet_task_time as tt"; + $sql .= ", ".MAIN_DB_PREFIX."element_time as tt"; $sql .= " WHERE t.fk_projet = p.rowid"; $sql .= " AND p.entity = ".((int) $conf->entity); - $sql .= " AND tt.fk_task = t.rowid"; + $sql .= " AND tt.fk_element = t.rowid"; + $sql .= " AND tt.elementtype = 'task'"; $sql .= " AND tt.fk_user = ".((int) $user->id); - $sql .= " AND task_date BETWEEN '".$db->idate(dol_get_first_day($year, $month))."' AND '".$db->idate(dol_get_last_day($year, $month))."'"; + $sql .= " AND element_date BETWEEN '".$db->idate(dol_get_first_day($year, $month))."' AND '".$db->idate(dol_get_last_day($year, $month))."'"; $sql .= " AND p.rowid in (".$db->sanitize($projectsListId).")"; $sql .= " GROUP BY p.rowid, p.ref, p.title, p.public"; @@ -327,15 +331,16 @@ if (!empty($conf->global->PROJECT_TASK_TIME_YEAR)) { print ''.$langs->trans("Time").''; print "\n"; - $sql = "SELECT p.rowid, p.ref, p.title, p.public, SUM(tt.task_duration) as nb"; + $sql = "SELECT p.rowid, p.ref, p.title, p.public, SUM(tt.element_duration) as nb"; $sql .= " FROM ".MAIN_DB_PREFIX."projet as p"; $sql .= ", ".MAIN_DB_PREFIX."projet_task as t"; - $sql .= ", ".MAIN_DB_PREFIX."projet_task_time as tt"; + $sql .= ", ".MAIN_DB_PREFIX."element_time as tt"; $sql .= " WHERE t.fk_projet = p.rowid"; $sql .= " AND p.entity = ".((int) $conf->entity); - $sql .= " AND tt.fk_task = t.rowid"; + $sql .= " AND tt.fk_element = t.rowid"; + $sql .= " AND tt.elementtype = 'task'"; $sql .= " AND tt.fk_user = ".((int) $user->id); - $sql .= " AND YEAR(task_date) = '".strftime("%Y", $now)."'"; + $sql .= " AND YEAR(element_date) = '".strftime("%Y", $now)."'"; $sql .= " AND p.rowid in (".$db->sanitize($projectsListId).")"; $sql .= " GROUP BY p.rowid, p.ref, p.title, p.public"; @@ -407,11 +412,11 @@ if (empty($conf->global->PROJECT_HIDE_TASKS) && !empty($conf->global->PROJECT_SH $max = (empty($conf->global->PROJECT_LIMIT_TASK_PROJECT_AREA) ? 1000 : $conf->global->PROJECT_LIMIT_TASK_PROJECT_AREA); $sql = "SELECT p.ref, p.title, p.rowid as projectid, p.fk_statut as status, p.fk_opp_status as opp_status, p.public, p.dateo as projdateo, p.datee as projdatee,"; - $sql .= " t.label, t.rowid as taskid, t.planned_workload, t.duration_effective, t.progress, t.dateo, t.datee, SUM(tasktime.task_duration) as timespent"; + $sql .= " t.label, t.rowid as taskid, t.planned_workload, t.duration_effective, t.progress, t.dateo, t.datee, SUM(tasktime.element_duration) as timespent"; $sql .= " FROM ".MAIN_DB_PREFIX."projet as p"; $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s on p.fk_soc = s.rowid"; $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task as t on t.fk_projet = p.rowid"; - $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task_time as tasktime on tasktime.fk_task = t.rowid"; + $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."element_time as tasktime on (tasktime.fk_element = t.rowid AND tasktime.elementtype = 'task')"; $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."user as u on tasktime.fk_user = u.rowid"; if ($mine) { $sql .= ", ".MAIN_DB_PREFIX."element_contact as ect"; diff --git a/htdocs/projet/class/project.class.php b/htdocs/projet/class/project.class.php index c9a811ad245..e2faef3da8e 100644 --- a/htdocs/projet/class/project.class.php +++ b/htdocs/projet/class/project.class.php @@ -7,6 +7,7 @@ * Copyright (C) 2017 Ferran Marcet * Copyright (C) 2019 Juanjo Menent * Copyright (C) 2022 Charlene Benke + * Copyright (C) 2023 Gauthier VERDOL * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -782,8 +783,8 @@ class Project extends CommonObject $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 (".$this->db->sanitize($ids).")"; } elseif ($type == 'project_task') { $sql = "SELECT DISTINCT pt.rowid FROM ".MAIN_DB_PREFIX."projet_task as pt WHERE pt.fk_projet IN (".$this->db->sanitize($ids).")"; - } elseif ($type == 'project_task_time') { // Case we want to duplicate line foreach user - $sql = "SELECT DISTINCT pt.rowid, ptt.fk_user FROM ".MAIN_DB_PREFIX."projet_task as pt, ".MAIN_DB_PREFIX."projet_task_time as ptt WHERE pt.rowid = ptt.fk_task AND pt.fk_projet IN (".$this->db->sanitize($ids).")"; + } elseif ($type == 'element_time') { // Case we want to duplicate line foreach user + $sql = "SELECT DISTINCT pt.rowid, ptt.fk_user FROM ".MAIN_DB_PREFIX."projet_task as pt, ".MAIN_DB_PREFIX."element_time as ptt WHERE pt.rowid = ptt.fk_element AND ptt.elementtype = 'task' AND pt.fk_projet IN (".$this->db->sanitize($ids).")"; } elseif ($type == 'stock_mouvement') { $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 (".$this->db->sanitize($ids).") AND ms.type_mouvement = 1"; } elseif ($type == 'loan') { @@ -1041,8 +1042,8 @@ class Project extends CommonObject $sql = "SELECT COUNT(ed.rowid) as nb 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 = ".((int) $this->id); } elseif ($type == 'project_task') { $sql = "SELECT DISTINCT COUNT(pt.rowid) as nb FROM ".MAIN_DB_PREFIX."projet_task as pt WHERE pt.fk_projet = ".((int) $this->id); - } elseif ($type == 'project_task_time') { // Case we want to duplicate line foreach user - $sql = "SELECT DISTINCT COUNT(pt.rowid) as nb FROM ".MAIN_DB_PREFIX."projet_task as pt, ".MAIN_DB_PREFIX."projet_task_time as ptt WHERE pt.rowid = ptt.fk_task AND pt.fk_projet = ".((int) $this->id); + } elseif ($type == 'element_time') { // Case we want to duplicate line foreach user + $sql = "SELECT DISTINCT COUNT(pt.rowid) as nb FROM ".MAIN_DB_PREFIX."projet_task as pt, ".MAIN_DB_PREFIX."element_time as ptt WHERE pt.rowid = ptt.fk_element AND ptt.elementtype = 'task' AND pt.fk_projet = ".((int) $this->id); } elseif ($type == 'stock_mouvement') { $sql = "SELECT COUNT(ms.rowid) as nb 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 = ".((int) $this->id)." AND ms.type_mouvement = 1"; } elseif ($type == 'loan') { @@ -2017,14 +2018,15 @@ class Project extends CommonObject 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 = "SELECT ptt.rowid as taskid, ptt.element_duration, ptt.element_date, ptt.element_datehour, ptt.fk_element"; + $sql .= " FROM ".MAIN_DB_PREFIX."element_time AS ptt, ".MAIN_DB_PREFIX."projet_task as pt"; + $sql .= " WHERE ptt.fk_element = pt.rowid"; + $sql .= " AND ptt.elementtype = 'task'"; $sql .= " AND pt.fk_projet = ".((int) $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)."')"; + $sql .= " AND (ptt.element_date >= '".$this->db->idate($datestart)."' "; + $sql .= " AND ptt.element_date <= '".$this->db->idate(dol_time_plus_duree($datestart, 1, 'w') - 1)."')"; if ($taskid) { - $sql .= " AND ptt.fk_task=".((int) $taskid); + $sql .= " AND ptt.fk_element=".((int) $taskid); } if (is_numeric($userid)) { $sql .= " AND ptt.fk_user=".((int) $userid); @@ -2040,13 +2042,13 @@ class Project extends CommonObject // 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 + $day = $this->db->jdate($obj->element_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; + $this->weekWorkLoad[$day] = $obj->element_duration; + $this->weekWorkLoadPerTask[$day][$obj->fk_element] = $obj->element_duration; } else { - $this->weekWorkLoad[$day] += $obj->task_duration; - $this->weekWorkLoadPerTask[$day][$obj->fk_task] += $obj->task_duration; + $this->weekWorkLoad[$day] += $obj->element_duration; + $this->weekWorkLoadPerTask[$day][$obj->fk_element] += $obj->element_duration; } $daylareadyfound[$day] = 1; $i++; @@ -2080,14 +2082,15 @@ class Project extends CommonObject 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 = "SELECT ptt.rowid as taskid, ptt.element_duration, ptt.element_date, ptt.element_datehour, ptt.fk_element"; + $sql .= " FROM ".MAIN_DB_PREFIX."element_time AS ptt, ".MAIN_DB_PREFIX."projet_task as pt"; + $sql .= " WHERE ptt.fk_element = pt.rowid"; + $sql .= " AND ptt.elementtype = 'task'"; $sql .= " AND pt.fk_projet = ".((int) $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, 'm') - 1)."')"; + $sql .= " AND (ptt.element_date >= '".$this->db->idate($datestart)."' "; + $sql .= " AND ptt.element_date <= '".$this->db->idate(dol_time_plus_duree($datestart, 1, 'm') - 1)."')"; if ($taskid) { - $sql .= " AND ptt.fk_task=".((int) $taskid); + $sql .= " AND ptt.fk_element=".((int) $taskid); } if (is_numeric($userid)) { $sql .= " AND ptt.fk_user=".((int) $userid); @@ -2103,16 +2106,16 @@ class Project extends CommonObject // Loop on each record found, so each couple (project id, task id) while ($i < $num) { $obj = $this->db->fetch_object($resql); - if (!empty($obj->task_date)) { - $date = explode('-', $obj->task_date); + if (!empty($obj->element_date)) { + $date = explode('-', $obj->element_date); $week_number = getWeekNumber($date[2], $date[1], $date[0]); } if (empty($weekalreadyfound[$week_number])) { - $this->monthWorkLoad[$week_number] = $obj->task_duration; - $this->monthWorkLoadPerTask[$week_number][$obj->fk_task] = $obj->task_duration; + $this->monthWorkLoad[$week_number] = $obj->element_duration; + $this->monthWorkLoadPerTask[$week_number][$obj->fk_element] = $obj->element_duration; } else { - $this->monthWorkLoad[$week_number] += $obj->task_duration; - $this->monthWorkLoadPerTask[$week_number][$obj->fk_task] += $obj->task_duration; + $this->monthWorkLoad[$week_number] += $obj->element_duration; + $this->monthWorkLoadPerTask[$week_number][$obj->fk_element] += $obj->element_duration; } $weekalreadyfound[$week_number] = 1; $i++; diff --git a/htdocs/projet/class/task.class.php b/htdocs/projet/class/task.class.php index 596d828ff97..a303f8708fa 100644 --- a/htdocs/projet/class/task.class.php +++ b/htdocs/projet/class/task.class.php @@ -5,6 +5,7 @@ * Copyright (C) 2018-2023 Frédéric France * Copyright (C) 2020 Juanjo Menent * Copyright (C) 2022 Charlene Benke + * Copyright (C) 2023 Gauthier VERDOL * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -29,6 +30,7 @@ require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/commonobjectline.class.php'; require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php'; +require_once DOL_DOCUMENT_ROOT.'/core/class/timespent.class.php'; /** @@ -49,7 +51,7 @@ class Task extends CommonObjectLine /** * @var string Field with ID of parent key if this field has a parent */ - public $fk_element = 'fk_task'; + public $fk_element = 'fk_element'; /** * @var string String with name of icon for myobject. @@ -60,7 +62,7 @@ class Task extends CommonObjectLine * @var array List of child tables. To test if we can delete object. */ protected $childtables = array( - 'projet_task_time' => array('name' => 'Task', 'parent' => 'projet_task', 'parentkey' => 'fk_task') + 'element_time' => array('name' => 'Task', 'parent' => 'projet_task', 'parentkey' => 'fk_element', 'parenttypefield' => 'elementtype', 'parenttypevalue' => 'task') ); /** @@ -556,8 +558,8 @@ class Task extends CommonObjectLine } if (!$error) { - $sql = "DELETE FROM ".MAIN_DB_PREFIX."projet_task_time"; - $sql .= " WHERE fk_task = ".((int) $this->id); + $sql = "DELETE FROM ".MAIN_DB_PREFIX."element_time"; + $sql .= " WHERE fk_element = ".((int) $this->id)." AND elementtype = 'task'"; $resql = $this->db->query($sql); if (!$resql) { @@ -673,8 +675,9 @@ class Task extends CommonObjectLine $ret = 0; $sql = "SELECT COUNT(*) as nb"; - $sql .= " FROM ".MAIN_DB_PREFIX."projet_task_time"; - $sql .= " WHERE fk_task = ".((int) $this->id); + $sql .= " FROM ".MAIN_DB_PREFIX."element_time"; + $sql .= " WHERE fk_element = ".((int) $this->id); + $sql .= " AND elementtype = 'task'"; dol_syslog(get_class($this)."::hasTimeSpent", LOG_DEBUG); $resql = $this->db->query($sql); @@ -877,7 +880,7 @@ class Task extends CommonObjectLine } } if ($includebilltime) { - $sql .= ", SUM(tt.task_duration * ".$this->db->ifsql("invoice_id IS NULL", "1", "0").") as tobill, SUM(tt.task_duration * ".$this->db->ifsql("invoice_id IS NULL", "0", "1").") as billed"; + $sql .= ", SUM(tt.element_duration * ".$this->db->ifsql("invoice_id IS NULL", "1", "0").") as tobill, SUM(tt.element_duration * ".$this->db->ifsql("invoice_id IS NULL", "0", "1").") as billed"; } $sql .= " FROM ".MAIN_DB_PREFIX."projet as p"; @@ -891,7 +894,7 @@ class Task extends CommonObjectLine } $sql .= ", ".MAIN_DB_PREFIX."projet_task as t"; if ($includebilltime) { - $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task_time as tt ON tt.fk_task = t.rowid"; + $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."element_time as tt ON (tt.fk_element = t.rowid AND tt.elementtype='task')"; } if ($filterontaskuser > 0) { $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec2"; @@ -908,14 +911,14 @@ class Task extends CommonObjectLine if ($filterontaskuser > 0) { $sql .= ", ".MAIN_DB_PREFIX."projet_task as t"; if ($includebilltime) { - $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task_time as tt ON tt.fk_task = t.rowid"; + $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."element_time as tt ON (tt.fk_element = t.rowid AND tt.elementtype='task')"; } $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec2"; $sql .= ", ".MAIN_DB_PREFIX."c_type_contact as ctc2"; } else { $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task as t on t.fk_projet = p.rowid"; if ($includebilltime) { - $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task_time as tt ON tt.fk_task = t.rowid"; + $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."element_time as tt ON (tt.fk_element = t.rowid AND tt.elementtype = 'task')"; } } $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task_extrafields as efpt ON (t.rowid = efpt.fk_object)"; @@ -1261,31 +1264,20 @@ class Task extends CommonObjectLine $this->db->begin(); - $sql = "INSERT INTO ".MAIN_DB_PREFIX."projet_task_time ("; - $sql .= "fk_task"; - $sql .= ", task_date"; - $sql .= ", task_datehour"; - $sql .= ", task_date_withhour"; - $sql .= ", task_duration"; - $sql .= ", fk_user"; - $sql .= ", fk_product"; - $sql .= ", note"; - $sql .= ", datec"; - $sql .= ") VALUES ("; - $sql .= ((int) $this->id); - $sql .= ", '".$this->db->idate($this->timespent_date)."'"; - $sql .= ", '".$this->db->idate($this->timespent_datehour)."'"; - $sql .= ", ".(empty($this->timespent_withhour) ? 0 : 1); - $sql .= ", ".((int) $this->timespent_duration); - $sql .= ", ".((int) $this->timespent_fk_user); - $sql .= ", ".((int) $this->timespent_fk_product); - $sql .= ", ".(isset($this->timespent_note) ? "'".$this->db->escape($this->timespent_note)."'" : "null"); - $sql .= ", '".$this->db->idate($now)."'"; - $sql .= ")"; + $timespent = new TimeSpent($this->db); + $timespent->fk_element = $this->id; + $timespent->elementtype = 'task'; + $timespent->element_date = $this->timespent_date; + $timespent->element_datehour = $this->timespent_datehour; + $timespent->element_date_withhour = $this->timespent_withhour; + $timespent->element_duration = $this->timespent_duration; + $timespent->fk_user = $this->timespent_fk_user; + $timespent->fk_product = $this->timespent_fk_product; + $timespent->note = $this->timespent_note; + $timespent->datec = $this->db->idate($now); - $resql = $this->db->query($sql); - if ($resql) { - $tasktime_id = $this->db->last_insert_id(MAIN_DB_PREFIX."projet_task_time"); + if ($timespent->create($user) > 0) { + $tasktime_id = $this->db->last_insert_id(MAIN_DB_PREFIX."element_time"); $ret = $tasktime_id; $this->timespent_id = $ret; @@ -1305,7 +1297,7 @@ class Task extends CommonObjectLine if ($ret > 0) { // Recalculate amount of time spent for task and update denormalized field $sql = "UPDATE ".MAIN_DB_PREFIX."projet_task"; - $sql .= " SET duration_effective = (SELECT SUM(task_duration) FROM ".MAIN_DB_PREFIX."projet_task_time as ptt where ptt.fk_task = ".((int) $this->id).")"; + $sql .= " SET duration_effective = (SELECT SUM(element_duration) FROM ".MAIN_DB_PREFIX."element_time as ptt where ptt.elementtype = 'task' AND ptt.fk_element = ".((int) $this->id).")"; if (isset($this->progress)) { $sql .= ", progress = ".((float) $this->progress); // Do not overwrite value if not provided } @@ -1318,12 +1310,15 @@ class Task extends CommonObjectLine } // Update hourly rate of this time spent entry - $sql = "UPDATE ".MAIN_DB_PREFIX."projet_task_time"; - $sql .= " SET thm = (SELECT thm FROM ".MAIN_DB_PREFIX."user WHERE rowid = ".((int) $this->timespent_fk_user).")"; // set average hour rate of user - $sql .= " WHERE rowid = ".((int) $tasktime_id); + $resql_thm_user = $this->db->query("SELECT thm FROM " . MAIN_DB_PREFIX . "user WHERE rowid = " . ((int)$timespent->fk_user)); + if (!empty($resql_thm_user)) { + $obj_thm_user = $this->db->fetch_object($resql_thm_user); + $timespent->thm = $obj_thm_user->thm; + } + $res_update = $timespent->update($user); dol_syslog(get_class($this)."::addTimeSpent", LOG_DEBUG); - if (!$this->db->query($sql)) { + if ($res_update <= 0) { $this->error = $this->db->lasterror(); $ret = -2; } @@ -1354,11 +1349,11 @@ class Task extends CommonObjectLine $sql .= " s.nom as thirdparty_name,"; $sql .= " s.email as thirdparty_email,"; $sql .= " ptt.rowid,"; - $sql .= " ptt.fk_task,"; - $sql .= " ptt.task_date,"; - $sql .= " ptt.task_datehour,"; - $sql .= " ptt.task_date_withhour,"; - $sql .= " ptt.task_duration,"; + $sql .= " ptt.fk_element as fk_task,"; + $sql .= " ptt.element_date as task_date,"; + $sql .= " ptt.element_datehour as task_datehour,"; + $sql .= " ptt.element_date_withhour as task_date_withhour,"; + $sql .= " ptt.element_duration as task_duration,"; $sql .= " ptt.fk_user,"; $sql .= " ptt.note,"; $sql .= " ptt.thm,"; @@ -1369,9 +1364,10 @@ class Task extends CommonObjectLine $sql .= " p.ref as project_ref,"; $sql .= " p.title as project_label,"; $sql .= " p.public as public"; - $sql .= " FROM ".MAIN_DB_PREFIX."projet_task_time as ptt, ".MAIN_DB_PREFIX."projet_task as pt, ".MAIN_DB_PREFIX."projet as p"; + $sql .= " FROM ".MAIN_DB_PREFIX."element_time as ptt, ".MAIN_DB_PREFIX."projet_task as pt, ".MAIN_DB_PREFIX."projet as p"; $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid"; - $sql .= " WHERE ptt.fk_task = pt.rowid AND pt.fk_projet = p.rowid"; + $sql .= " WHERE ptt.fk_element = pt.rowid AND pt.fk_projet = p.rowid"; + $sql .= " AND ptt.elementtype = 'task'"; $sql .= " AND pt.rowid = ".((int) $this->id); $sql .= " AND pt.entity IN (".getEntity('project').")"; if ($morewherefilter) { @@ -1452,19 +1448,19 @@ class Task extends CommonObjectLine $result = array(); $sql = "SELECT"; - $sql .= " MIN(t.task_datehour) as min_date,"; - $sql .= " MAX(t.task_datehour) as max_date,"; - $sql .= " SUM(t.task_duration) as total_duration,"; - $sql .= " SUM(t.task_duration / 3600 * ".$this->db->ifsql("t.thm IS NULL", 0, "t.thm").") as total_amount,"; + $sql .= " MIN(t.element_datehour) as min_date,"; + $sql .= " MAX(t.element_datehour) as max_date,"; + $sql .= " SUM(t.element_duration) as total_duration,"; + $sql .= " SUM(t.element_duration / 3600 * ".$this->db->ifsql("t.thm IS NULL", 0, "t.thm").") as total_amount,"; $sql .= " COUNT(t.rowid) as nblines,"; $sql .= " SUM(".$this->db->ifsql("t.thm IS NULL", 1, 0).") as nblinesnull"; - $sql .= " FROM ".MAIN_DB_PREFIX."projet_task_time as t"; - $sql .= " WHERE 1 = 1"; + $sql .= " FROM ".MAIN_DB_PREFIX."element_time as t"; + $sql .= " WHERE t.elementtype='task'"; if ($morewherefilter) { $sql .= $morewherefilter; } if ($id > 0) { - $sql .= " AND t.fk_task = ".((int) $id); + $sql .= " AND t.fk_element = ".((int) $id); } if ($userid > 0) { $sql .= " AND t.fk_user = ".((int) $userid); @@ -1512,19 +1508,19 @@ class Task extends CommonObjectLine $result = array(); $sql = "SELECT"; - $sql .= " SUM(t.task_duration) as nbseconds,"; - $sql .= " SUM(t.task_duration / 3600 * ".$this->db->ifsql("t.thm IS NULL", 0, "t.thm").") as amount, SUM(".$this->db->ifsql("t.thm IS NULL", 1, 0).") as nblinesnull"; - $sql .= " FROM ".MAIN_DB_PREFIX."projet_task_time as t"; - $sql .= " WHERE t.fk_task = ".((int) $id); + $sql .= " SUM(t.element_duration) as nbseconds,"; + $sql .= " SUM(t.element_duration / 3600 * ".$this->db->ifsql("t.thm IS NULL", 0, "t.thm").") as amount, SUM(".$this->db->ifsql("t.thm IS NULL", 1, 0).") as nblinesnull"; + $sql .= " FROM ".MAIN_DB_PREFIX."element_time as t"; + $sql .= " WHERE t.elementtype='task' AND t.fk_element = ".((int) $id); if (is_object($fuser) && $fuser->id > 0) { $sql .= " AND fk_user = ".((int) $fuser->id); } if ($dates > 0) { - $datefieldname = "task_datehour"; + $datefieldname = "element_datehour"; $sql .= " AND (".$datefieldname." >= '".$this->db->idate($dates)."' OR ".$datefieldname." IS NULL)"; } if ($datee > 0) { - $datefieldname = "task_datehour"; + $datefieldname = "element_datehour"; $sql .= " AND (".$datefieldname." <= '".$this->db->idate($datee)."' OR ".$datefieldname." IS NULL)"; } //print $sql; @@ -1556,45 +1552,30 @@ class Task extends CommonObjectLine { global $langs; - $sql = "SELECT"; - $sql .= " t.rowid,"; - $sql .= " t.fk_task,"; - $sql .= " t.task_date,"; - $sql .= " t.task_datehour,"; - $sql .= " t.task_date_withhour,"; - $sql .= " t.task_duration,"; - $sql .= " t.fk_user,"; - $sql .= " t.fk_product,"; - $sql .= " t.thm,"; - $sql .= " t.note"; - $sql .= " FROM ".MAIN_DB_PREFIX."projet_task_time as t"; - $sql .= " WHERE t.rowid = ".((int) $id); + $timespent = new TimeSpent($this->db); + $timespent->fetch($id); dol_syslog(get_class($this)."::fetchTimeSpent", LOG_DEBUG); - $resql = $this->db->query($sql); - if ($resql) { - if ($this->db->num_rows($resql)) { - $obj = $this->db->fetch_object($resql); - $this->timespent_id = $obj->rowid; - $this->id = $obj->fk_task; - $this->timespent_date = $this->db->jdate($obj->task_date); - $this->timespent_datehour = $this->db->jdate($obj->task_datehour); - $this->timespent_withhour = $obj->task_date_withhour; - $this->timespent_duration = $obj->task_duration; - $this->timespent_fk_user = $obj->fk_user; - $this->timespent_fk_product = $obj->fk_product; - $this->timespent_thm = $obj->thm; // hourly rate - $this->timespent_note = $obj->note; - } + if ($timespent->id > 0) { - $this->db->free($resql); + $this->timespent_id = $timespent->id; + $this->id = $timespent->fk_element; + $this->timespent_date = $this->db->jdate($timespent->element_date); + $this->timespent_datehour = $this->db->jdate($timespent->element_datehour); + $this->timespent_withhour = $timespent->element_date_withhour; + $this->timespent_duration = $timespent->element_duration; + $this->timespent_fk_user = $timespent->fk_user; + $this->timespent_fk_product = $timespent->fk_product; + $this->timespent_thm = $timespent->thm; // hourly rate + $this->timespent_note = $timespent->note; return 1; - } else { - $this->error = "Error ".$this->db->lasterror(); - return -1; + } + + return 0; + } /** @@ -1613,11 +1594,11 @@ class Task extends CommonObjectLine $sql .= " s.nom as thirdparty_name,"; $sql .= " s.email as thirdparty_email,"; $sql .= " ptt.rowid,"; - $sql .= " ptt.fk_task,"; - $sql .= " ptt.task_date,"; - $sql .= " ptt.task_datehour,"; - $sql .= " ptt.task_date_withhour,"; - $sql .= " ptt.task_duration,"; + $sql .= " ptt.fk_element as fk_task,"; + $sql .= " ptt.element_date as task_date,"; + $sql .= " ptt.element_datehour as task_datehour,"; + $sql .= " ptt.element_date_withhour as task_date_withhour,"; + $sql .= " ptt.element_duration as task_duration,"; $sql .= " ptt.fk_user,"; $sql .= " ptt.note,"; $sql .= " ptt.thm,"; @@ -1628,9 +1609,10 @@ class Task extends CommonObjectLine $sql .= " p.ref as project_ref,"; $sql .= " p.title as project_label,"; $sql .= " p.public as public"; - $sql .= " FROM ".MAIN_DB_PREFIX."projet_task_time as ptt, ".MAIN_DB_PREFIX."projet_task as pt, ".MAIN_DB_PREFIX."projet as p"; + $sql .= " FROM ".MAIN_DB_PREFIX."element_time as ptt, ".MAIN_DB_PREFIX."projet_task as pt, ".MAIN_DB_PREFIX."projet as p"; $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid"; - $sql .= " WHERE ptt.fk_task = pt.rowid AND pt.fk_projet = p.rowid"; + $sql .= " WHERE ptt.fk_element = pt.rowid AND pt.fk_projet = p.rowid"; + $sql .= " AND ptt.elementtype = 'task'"; $sql .= " AND ptt.fk_user = ".((int) $userobj->id); $sql .= " AND pt.entity IN (".getEntity('project').")"; if ($morewherefilter) { @@ -1729,18 +1711,18 @@ class Task extends CommonObjectLine $this->db->begin(); - $sql = "UPDATE ".MAIN_DB_PREFIX."projet_task_time SET"; - $sql .= " task_date = '".$this->db->idate($this->timespent_date)."',"; - $sql .= " task_datehour = '".$this->db->idate($this->timespent_datehour)."',"; - $sql .= " task_date_withhour = ".(empty($this->timespent_withhour) ? 0 : 1).","; - $sql .= " task_duration = ".((int) $this->timespent_duration).","; - $sql .= " fk_user = ".((int) $this->timespent_fk_user).","; - $sql .= " fk_product = ".((int) $this->timespent_fk_product).","; - $sql .= " note = ".(isset($this->timespent_note) ? "'".$this->db->escape($this->timespent_note)."'" : "null"); - $sql .= " WHERE rowid = ".((int) $this->timespent_id); + $timespent = new TimeSpent($this->db); + $timespent->fetch($this->timespent_id); + $timespent->element_date = $this->timespent_date; + $timespent->element_datehour = $this->timespent_datehour; + $timespent->element_date_withhour = $this->timespent_withhour; + $timespent->element_duration = $this->timespent_duration; + $timespent->fk_user = $this->timespent_fk_user; + $timespent->fk_product = $this->timespent_fk_product; + $timespent->note = $this->timespent_note; dol_syslog(get_class($this)."::updateTimeSpent", LOG_DEBUG); - if ($this->db->query($sql)) { + if ($timespent->update($user) > 0) { if (!$notrigger) { // Call trigger $result = $this->call_trigger('TASK_TIMESPENT_MODIFY', $user); @@ -1764,7 +1746,7 @@ class Task extends CommonObjectLine if ($this->timespent_old_duration != $this->timespent_duration) { // Recalculate amount of time spent for task and update denormalized field $sql = "UPDATE " . MAIN_DB_PREFIX . "projet_task"; - $sql .= " SET duration_effective = (SELECT SUM(task_duration) FROM " . MAIN_DB_PREFIX . "projet_task_time as ptt where ptt.fk_task = " . ((int) $this->id) . ")"; + $sql .= " SET duration_effective = (SELECT SUM(element_duration) FROM " . MAIN_DB_PREFIX . "element_time as ptt where ptt.elementtype = 'task' AND ptt.fk_element = " . ((int) $this->id) . ")"; if (isset($this->progress)) { $sql .= ", progress = " . ((float) $this->progress); // Do not overwrite value if not provided } @@ -1779,15 +1761,18 @@ class Task extends CommonObjectLine } // Update hourly rate of this time spent entry, but only if it was not set initialy - $sql = "UPDATE ".MAIN_DB_PREFIX."projet_task_time"; - $sql .= " SET thm = (SELECT thm FROM ".MAIN_DB_PREFIX."user WHERE rowid = ".((int) $this->timespent_fk_user).")"; // set average hour rate of user - $sql .= " WHERE rowid = ".((int) $this->timespent_id); - if (empty($conf->global->TIMESPENT_ALWAYS_UPDATE_THM)) { // then if not empty we always update, in case of new thm for user, or change user of task time line - $sql .= " AND (thm IS NULL OR thm = 0)"; + $res_update = 1; + if(empty($timespent->thm) || !empty($conf->global->TIMESPENT_ALWAYS_UPDATE_THM)) { + $resql_thm_user = $this->db->query("SELECT thm FROM " . MAIN_DB_PREFIX . "user WHERE rowid = " . ((int)$timespent->fk_user)); + if (!empty($resql_thm_user)) { + $obj_thm_user = $this->db->fetch_object($resql_thm_user); + $timespent->thm = $obj_thm_user->thm; + } + $res_update = $timespent->update($user); } - dol_syslog(get_class($this)."::addTimeSpent", LOG_DEBUG); - if (!$this->db->query($sql)) { + dol_syslog(get_class($this)."::updateTimeSpent", LOG_DEBUG); + if ($res_update <= 0) { $this->error = $this->db->lasterror(); $ret = -2; } @@ -1835,12 +1820,13 @@ class Task extends CommonObjectLine } if (!$error) { - $sql = "DELETE FROM ".MAIN_DB_PREFIX."projet_task_time"; - $sql .= " WHERE rowid = ".((int) $this->timespent_id); - dol_syslog(get_class($this)."::delTimeSpent", LOG_DEBUG); - $resql = $this->db->query($sql); - if (!$resql) { + $timespent = new TimeSpent($this->db); + $timespent->fetch($this->timespent_id); + + $res_del = $timespent->delete($user); + + if ($res_del < 0) { $error++; $this->errors[] = "Error ".$this->db->lasterror(); } } diff --git a/htdocs/projet/element.php b/htdocs/projet/element.php index 935275bfe54..b680e29399a 100644 --- a/htdocs/projet/element.php +++ b/htdocs/projet/element.php @@ -8,6 +8,7 @@ * Copyright (C) 2016 Josep Lluís Amador * Copyright (C) 2021 Gauthier VERDOL * Copyright (C) 2021 Noé Cendrier + * Copyright (C) 2023 Gauthier VERDOL * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -575,7 +576,7 @@ $listofreferent = array( 'class'=>'Task', 'margin'=>'minus', 'table'=>'projet_task', - 'datefieldname'=>'task_date', + 'datefieldname'=>'element_date', 'disableamount'=>0, 'urlnew'=>DOL_URL_ROOT.'/projet/tasks/time.php?withproject=1&action=createtime&projectid='.$id.'&backtopage='.urlencode($_SERVER['PHP_SELF'].'?id='.$id), 'buttonnew'=>'AddTimeSpent', @@ -1337,7 +1338,7 @@ foreach ($listofreferent as $key => $value) { $tmpuser2->fetch($element->fk_user_author); print $tmpuser2->getNomUrl(1, '', 48); } - } elseif ($tablename == 'projet_task' && $key == 'project_task_time') { // if $key == 'project_task', we don't want details per user + } elseif ($tablename == 'projet_task' && $key == 'element_time') { // if $key == 'project_task', we don't want details per user print $elementuser->getNomUrl(1); } print ''; diff --git a/htdocs/projet/tasks/list.php b/htdocs/projet/tasks/list.php index 00449a30a17..a860f1205c7 100644 --- a/htdocs/projet/tasks/list.php +++ b/htdocs/projet/tasks/list.php @@ -4,6 +4,7 @@ * Copyright (C) 2006-2010 Regis Houssin * Copyright (C) 2018 Ferran Marcet * Copyright (C) 2021 Alexandre Spangaro + * Copyright (C) 2023 Gauthier VERDOL * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -345,7 +346,7 @@ $sql .= " t.description, t.fk_task_parent"; $sql .= " ,t.budget_amount"; // Add sum fields if (!empty($arrayfields['t.tobill']['checked']) || !empty($arrayfields['t.billed']['checked'])) { - $sql .= " , SUM(tt.task_duration * ".$db->ifsql("invoice_id IS NULL", "1", "0").") as tobill, SUM(tt.task_duration * ".$db->ifsql("invoice_id IS NULL", "0", "1").") as billed"; + $sql .= " , SUM(tt.element_duration * ".$db->ifsql("invoice_id IS NULL", "1", "0").") as tobill, SUM(tt.element_duration * ".$db->ifsql("invoice_id IS NULL", "0", "1").") as billed"; } // Add fields from extrafields if (!empty($extrafields->attributes[$object->table_element]['label'])) { @@ -361,7 +362,7 @@ $sql .= " FROM ".MAIN_DB_PREFIX."projet as p"; $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s on p.fk_soc = s.rowid"; $sql .= ", ".MAIN_DB_PREFIX."projet_task as t"; if (!empty($arrayfields['t.tobill']['checked']) || !empty($arrayfields['t.billed']['checked'])) { - $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task_time as tt ON tt.fk_task = t.rowid"; + $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."element_time as tt ON (tt.fk_element = t.rowid AND tt.elementtype = 'task')"; } if (isset($extrafields->attributes[$object->table_element]['label']) && is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label'])) { $sql .= " LEFT JOIN ".MAIN_DB_PREFIX.$object->table_element."_extrafields as ef on (t.rowid = ef.fk_object)"; diff --git a/htdocs/projet/tasks/time.php b/htdocs/projet/tasks/time.php index 85d83031ccd..088807f02fc 100644 --- a/htdocs/projet/tasks/time.php +++ b/htdocs/projet/tasks/time.php @@ -6,6 +6,7 @@ * Copyright (C) 2018 Ferran Marcet * Copyright (C) 2018 Frédéric France * Copyright (C) 2019-2021 Christophe Battarel + * Copyright (C) 2023 Gauthier VERDOL * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -104,7 +105,7 @@ $offset = $limit * $page; $pageprev = $page - 1; $pagenext = $page + 1; if (!$sortfield) { - $sortfield = 't.task_date,t.task_datehour,t.rowid'; + $sortfield = 't.element_date,t.element_datehour,t.rowid'; } if (!$sortorder) { $sortorder = 'DESC,DESC,DESC'; @@ -561,7 +562,7 @@ if ($action == 'confirm_generateinvoice') { } // Update lineid into line of timespent - $sql = 'UPDATE '.MAIN_DB_PREFIX.'projet_task_time SET invoice_line_id = '.((int) $lineid).', invoice_id = '.((int) $tmpinvoice->id); + $sql = 'UPDATE '.MAIN_DB_PREFIX.'element_time SET invoice_line_id = '.((int) $lineid).', invoice_id = '.((int) $tmpinvoice->id); $sql .= ' WHERE rowid IN ('.$db->sanitize(join(',', $toselect)).') AND fk_user = '.((int) $userid); $result = $db->query($sql); if (!$result) { @@ -665,7 +666,7 @@ if ($action == 'confirm_generateinvoice') { //var_dump($lineid);exit; // Update lineid into line of timespent - $sql = 'UPDATE '.MAIN_DB_PREFIX.'projet_task_time SET invoice_line_id = '.((int) $lineid).', invoice_id = '.((int) $tmpinvoice->id); + $sql = 'UPDATE '.MAIN_DB_PREFIX.'element_time SET invoice_line_id = '.((int) $lineid).', invoice_id = '.((int) $tmpinvoice->id); $sql .= ' WHERE rowid IN ('.$db->sanitize(join(',', $toselect)).') AND fk_user = '.((int) $userid); $result = $db->query($sql); if (!$result) { @@ -764,7 +765,7 @@ if ($action == 'confirm_generateinvoice') { if (!$error) { // Update lineid into line of timespent - $sql = 'UPDATE ' . MAIN_DB_PREFIX . 'projet_task_time SET invoice_line_id = ' . ((int) $lineid) . ', invoice_id = ' . ((int) $tmpinvoice->id); + $sql = 'UPDATE ' . MAIN_DB_PREFIX . 'element_time SET invoice_line_id = ' . ((int) $lineid) . ', invoice_id = ' . ((int) $tmpinvoice->id); $sql .= ' WHERE rowid IN (' . $db->sanitize(join(',', $toselect)) . ')'; $result = $db->query($sql); if (!$result) { @@ -1262,7 +1263,7 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser // Definition of fields for list $arrayfields = array(); - $arrayfields['t.task_date'] = array('label'=>$langs->trans("Date"), 'checked'=>1); + $arrayfields['t.element_date'] = array('label'=>$langs->trans("Date"), 'checked'=>1); $arrayfields['p.fk_soc'] = array('label'=>$langs->trans("ThirdParty"), 'type'=>'integer:Societe:/societe/class/societe.class.php:1','checked'=>1); $arrayfields['s.name_alias'] = array('label'=>$langs->trans("AliasNameShort"), 'type'=>'integer:Societe:/societe/class/societe.class.php:1'); if ((empty($id) && empty($ref)) || !empty($projectidforalltimes)) { // Not a dedicated task @@ -1270,15 +1271,15 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser $arrayfields['p.project_ref'] = ['label' => $langs->trans('RefProject'), 'checked' => 1]; $arrayfields['p.project_label'] = ['label' => $langs->trans('ProjectLabel'), 'checked' => 1]; } - $arrayfields['t.task_ref'] = array('label'=>$langs->trans("RefTask"), 'checked'=>1); - $arrayfields['t.task_label'] = array('label'=>$langs->trans("LabelTask"), 'checked'=>1); + $arrayfields['t.element_ref'] = array('label'=>$langs->trans("RefTask"), 'checked'=>1); + $arrayfields['t.element_label'] = array('label'=>$langs->trans("LabelTask"), 'checked'=>1); } $arrayfields['author'] = array('label'=>$langs->trans("By"), 'checked'=>1); $arrayfields['t.note'] = array('label'=>$langs->trans("Note"), 'checked'=>1); if (isModEnabled('service') && !empty($projectstatic->thirdparty) && $projectstatic->thirdparty->id > 0 && $projectstatic->usage_bill_time) { $arrayfields['t.fk_product'] = array('label' => $langs->trans("Product"), 'checked' => 1); } - $arrayfields['t.task_duration'] = array('label'=>$langs->trans("Duration"), 'checked'=>1); + $arrayfields['t.element_duration'] = array('label'=>$langs->trans("Duration"), 'checked'=>1); $arrayfields['value'] = array('label'=>$langs->trans("Value"), 'checked'=>1, 'enabled'=>(empty($conf->salaries->enabled) ? 0 : 1)); $arrayfields['valuebilled'] = array('label'=>$langs->trans("Billed"), 'checked'=>1, 'enabled'=>(((!empty($conf->global->PROJECT_HIDE_TASKS) || empty($conf->global->PROJECT_BILL_TIME_SPENT)) ? 0 : 1) && $projectstatic->usage_bill_time)); // Extra fields @@ -1547,7 +1548,7 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser $varpage = empty($contextpage) ? $_SERVER["PHP_SELF"] : $contextpage; $selectedfields = $form->multiSelectArrayWithCheckbox('selectedfields', $arrayfields, $varpage, getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN', '')); // This also change content of $arrayfields - $sql = "SELECT t.rowid, t.fk_task, t.task_date, t.task_datehour, t.task_date_withhour, t.task_duration, t.fk_user, t.note, t.thm,"; + $sql = "SELECT t.rowid, t.fk_element, t.element_date, t.element_datehour, t.element_date_withhour, t.element_duration, t.fk_user, t.note, t.thm,"; $sql .= " t.fk_product,"; $sql .= " pt.ref, pt.label, pt.fk_projet,"; $sql .= " u.lastname, u.firstname, u.login, u.photo, u.statut as user_status,"; @@ -1558,11 +1559,11 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser $reshook = $hookmanager->executeHooks('printFieldListSelect', $parameters, $object); // Note that $action and $object may have been modified by hook $sql .= preg_replace('/^,/', '', $hookmanager->resPrint); $sql = preg_replace('/,\s*$/', '', $sql); - $sql .= " FROM ".MAIN_DB_PREFIX."projet_task_time as t"; + $sql .= " FROM ".MAIN_DB_PREFIX."element_time as t"; $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."facturedet as il ON il.rowid = t.invoice_line_id"; $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."facture as inv ON inv.rowid = il.fk_facture"; $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as prod ON prod.rowid = t.fk_product"; - $sql .= " INNER JOIN ".MAIN_DB_PREFIX."projet_task as pt ON pt.rowid = t.fk_task"; + $sql .= " INNER JOIN ".MAIN_DB_PREFIX."projet_task as pt ON pt.rowid = t.fk_element"; $sql .= " INNER JOIN ".MAIN_DB_PREFIX."projet as p ON p.rowid = pt.fk_projet"; $sql .= " INNER JOIN ".MAIN_DB_PREFIX."user as u ON t.fk_user = u.rowid"; $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON s.rowid = p.fk_soc"; @@ -1571,10 +1572,10 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser $parameters = array(); $reshook = $hookmanager->executeHooks('printFieldListFrom', $parameters, $object); // Note that $action and $object may have been modified by hook $sql .= $hookmanager->resPrint; - $sql .= " WHERE 1 = 1 "; + $sql .= " WHERE elementtype='task' "; if (empty($projectidforalltimes) && empty($allprojectforuser)) { // Limit on one task - $sql .= " AND t.fk_task =".((int) $object->id); + $sql .= " AND t.fk_element =".((int) $object->id); } elseif (!empty($projectidforalltimes)) { // Limit on one project $sql .= " AND pt.fk_projet IN (".$db->sanitize($projectidforalltimes).")"; @@ -1625,27 +1626,27 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser } if ($search_date_start) { - $sql .= " AND t.task_date >= '".$db->idate($search_date_start)."'"; + $sql .= " AND t.element_date >= '".$db->idate($search_date_start)."'"; } if ($search_date_end) { - $sql .= " AND t.task_date <= '".$db->idate($search_date_end)."'"; + $sql .= " AND t.element_date <= '".$db->idate($search_date_end)."'"; } - if (!empty($arrayfields['t.task_duration']['checked'])) { + if (!empty($arrayfields['t.element_duration']['checked'])) { if ($search_timespent_starthour || $search_timespent_startmin) { $timespent_duration_start = $search_timespent_starthour * 60 * 60; // We store duration in seconds $timespent_duration_start += ($search_timespent_startmin ? $search_timespent_startmin : 0) * 60; // We store duration in seconds - $sql .= " AND t.task_duration >= " . $timespent_duration_start; + $sql .= " AND t.element_duration >= " . $timespent_duration_start; } if ($search_timespent_endhour || $search_timespent_endmin) { $timespent_duration_end = $search_timespent_endhour * 60 * 60; // We store duration in seconds $timespent_duration_end += ($search_timespent_endmin ? $search_timespent_endmin : 0) * 60; // We store duration in seconds - $sql .= " AND t.task_duration <= " . $timespent_duration_end; + $sql .= " AND t.element_duration <= " . $timespent_duration_end; } } - $sql .= dolSqlDateFilter('t.task_datehour', $search_day, $search_month, $search_year); + $sql .= dolSqlDateFilter('t.element_datehour', $search_day, $search_month, $search_year); // Add where from hooks $parameters = array(); @@ -1873,7 +1874,7 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser print ''; } // Date - if (!empty($arrayfields['t.task_date']['checked'])) { + if (!empty($arrayfields['t.element_date']['checked'])) { print ''; print '
'; print $form->selectDate($search_date_start ? $search_date_start : -1, 'search_date_start', 0, 0, 1, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans('From')); @@ -1903,10 +1904,10 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser } // Task if ((empty($id) && empty($ref)) || !empty($projectidforalltimes)) { // Not a dedicated task - if (!empty($arrayfields['t.task_ref']['checked'])) { + if (!empty($arrayfields['t.element_ref']['checked'])) { print ''; } - if (!empty($arrayfields['t.task_label']['checked'])) { + if (!empty($arrayfields['t.element_label']['checked'])) { print ''; } } @@ -1919,7 +1920,7 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser print ''; } // Duration - if (!empty($arrayfields['t.task_duration']['checked'])) { + if (!empty($arrayfields['t.element_duration']['checked'])) { // Duration - Time spent print ''; @@ -1975,12 +1976,12 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) { print_liste_field_titre($selectedfields, $_SERVER["PHP_SELF"], "", '', '', 'width="80"', $sortfield, $sortorder, 'center maxwidthsearch '); } - if (!empty($arrayfields['t.task_date']['checked'])) { - print_liste_field_titre($arrayfields['t.task_date']['label'], $_SERVER['PHP_SELF'], 't.task_date,t.task_datehour,t.rowid', '', $param, '', $sortfield, $sortorder); + if (!empty($arrayfields['t.element_date']['checked'])) { + print_liste_field_titre($arrayfields['t.element_date']['label'], $_SERVER['PHP_SELF'], 't.element_date,t.element_datehour,t.rowid', '', $param, '', $sortfield, $sortorder); } if (!empty($arrayfields['p.fk_soc']['checked'])) { - print_liste_field_titre($arrayfields['p.fk_soc']['label'], $_SERVER['PHP_SELF'], 't.task_date,t.task_datehour,t.rowid', '', $param, '', $sortfield, $sortorder); + print_liste_field_titre($arrayfields['p.fk_soc']['label'], $_SERVER['PHP_SELF'], 't.element_date,t.element_datehour,t.rowid', '', $param, '', $sortfield, $sortorder); } if (!empty($arrayfields['s.name_alias']['checked'])) { print_liste_field_titre($arrayfields['s.name_alias']['label'], $_SERVER['PHP_SELF'], 's.name_alias', '', $param, '', $sortfield, $sortorder); @@ -1994,11 +1995,11 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser } } if ((empty($id) && empty($ref)) || !empty($projectidforalltimes)) { // Not a dedicated task - if (!empty($arrayfields['t.task_ref']['checked'])) { - print_liste_field_titre($arrayfields['t.task_ref']['label'], $_SERVER['PHP_SELF'], 'pt.ref', '', $param, '', $sortfield, $sortorder); + if (!empty($arrayfields['t.element_ref']['checked'])) { + print_liste_field_titre($arrayfields['t.element_ref']['label'], $_SERVER['PHP_SELF'], 'pt.ref', '', $param, '', $sortfield, $sortorder); } - if (!empty($arrayfields['t.task_label']['checked'])) { - print_liste_field_titre($arrayfields['t.task_label']['label'], $_SERVER['PHP_SELF'], 'pt.label', '', $param, '', $sortfield, $sortorder); + if (!empty($arrayfields['t.element_label']['checked'])) { + print_liste_field_titre($arrayfields['t.element_label']['label'], $_SERVER['PHP_SELF'], 'pt.label', '', $param, '', $sortfield, $sortorder); } } if (!empty($arrayfields['author']['checked'])) { @@ -2007,8 +2008,8 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser if (!empty($arrayfields['t.note']['checked'])) { print_liste_field_titre($arrayfields['t.note']['label'], $_SERVER['PHP_SELF'], 't.note', '', $param, '', $sortfield, $sortorder); } - if (!empty($arrayfields['t.task_duration']['checked'])) { - print_liste_field_titre($arrayfields['t.task_duration']['label'], $_SERVER['PHP_SELF'], 't.task_duration', '', $param, '', $sortfield, $sortorder, 'right '); + if (!empty($arrayfields['t.element_duration']['checked'])) { + print_liste_field_titre($arrayfields['t.element_duration']['label'], $_SERVER['PHP_SELF'], 't.element_duration', '', $param, '', $sortfield, $sortorder, 'right '); } if (!empty($arrayfields['t.fk_product']['checked'])) { print_liste_field_titre($arrayfields['t.fk_product']['label'], $_SERVER['PHP_SELF'], 't.fk_product', '', $param, '', $sortfield, $sortorder); @@ -2046,8 +2047,8 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser break; } - $date1 = $db->jdate($task_time->task_date); - $date2 = $db->jdate($task_time->task_datehour); + $date1 = $db->jdate($task_time->element_date); + $date2 = $db->jdate($task_time->element_datehour); print ''; @@ -2068,11 +2069,11 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser print ''; } - print 'rowid.$param.((empty($id) || $tab == 'timespent') ? '&tab=timespent' : '').'">'; + print 'rowid.$param.((empty($id) || $tab == 'timespent') ? '&tab=timespent' : '').'">'; print img_edit('default', 0, 'class="pictofixedwidth paddingleft"'); print ''; - print 'rowid.$param.((empty($id) || $tab == 'timespent') ? '&tab=timespent' : '').'">'; + print 'rowid.$param.((empty($id) || $tab == 'timespent') ? '&tab=timespent' : '').'">'; print img_delete('default', 'class="pictodelete paddingleft"'); print ''; @@ -2088,16 +2089,16 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser } } // Date - if (!empty($arrayfields['t.task_date']['checked'])) { + if (!empty($arrayfields['t.element_date']['checked'])) { print ''; if ($action == 'editline' && GETPOST('lineid', 'int') == $task_time->rowid) { - if (empty($task_time->task_date_withhour)) { + if (empty($task_time->element_date_withhour)) { print $form->selectDate(($date2 ? $date2 : $date1), 'timeline', 3, 3, 2, "timespent_date", 1, 0); } else { print $form->selectDate(($date2 ? $date2 : $date1), 'timeline', 1, 1, 2, "timespent_date", 1, 0); } } else { - print dol_print_date(($date2 ? $date2 : $date1), ($task_time->task_date_withhour ? 'dayhour' : 'day')); + print dol_print_date(($date2 ? $date2 : $date1), ($task_time->element_date_withhour ? 'dayhour' : 'day')); } print ''; if (!$i) { @@ -2178,13 +2179,13 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser } // Task ref - if (!empty($arrayfields['t.task_ref']['checked'])) { + if (!empty($arrayfields['t.element_ref']['checked'])) { if ((empty($id) && empty($ref)) || !empty($projectidforalltimes)) { // Not a dedicated task print ''; if ($action == 'editline' && GETPOST('lineid', 'int') == $task_time->rowid) { - $formproject->selectTasks(-1, GETPOST('taskid', 'int') ? GETPOST('taskid', 'int') : $task_time->fk_task, 'taskid', 0, 0, 1, 1, 0, 0, 'maxwidth300', $projectstatic->id, ''); + $formproject->selectTasks(-1, GETPOST('taskid', 'int') ? GETPOST('taskid', 'int') : $task_time->fk_element, 'taskid', 0, 0, 1, 1, 0, 0, 'maxwidth300', $projectstatic->id, ''); } else { - $tasktmp->id = $task_time->fk_task; + $tasktmp->id = $task_time->fk_element; $tasktmp->ref = $task_time->ref; $tasktmp->label = $task_time->label; print $tasktmp->getNomUrl(1, 'withproject', 'time'); @@ -2199,7 +2200,7 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser } // Task label - if (!empty($arrayfields['t.task_label']['checked'])) { + if (!empty($arrayfields['t.element_label']['checked'])) { if ((empty($id) && empty($ref)) || !empty($projectidforalltimes)) { // Not a dedicated task print ''; print dol_escape_htmltag($task_time->label); @@ -2258,33 +2259,33 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser } // Time spent - if (!empty($arrayfields['t.task_duration']['checked'])) { + if (!empty($arrayfields['t.element_duration']['checked'])) { print ''; if ($action == 'editline' && GETPOST('lineid', 'int') == $task_time->rowid) { - print ''; - print $form->select_duration('new_duration', $task_time->task_duration, 0, 'text'); + print ''; + print $form->select_duration('new_duration', $task_time->element_duration, 0, 'text'); } else { - print convertSecondToTime($task_time->task_duration, 'allhourmin'); + print convertSecondToTime($task_time->element_duration, 'allhourmin'); } print ''; if (!$i) { $totalarray['nbfield']++; } if (!$i) { - $totalarray['pos'][$totalarray['nbfield']] = 't.task_duration'; + $totalarray['pos'][$totalarray['nbfield']] = 't.element_duration'; } - if (empty($totalarray['val']['t.task_duration'])) { - $totalarray['val']['t.task_duration'] = $task_time->task_duration; + if (empty($totalarray['val']['t.element_duration'])) { + $totalarray['val']['t.element_duration'] = $task_time->element_duration; } else { - $totalarray['val']['t.task_duration'] += $task_time->task_duration; + $totalarray['val']['t.element_duration'] += $task_time->element_duration; } if (!$i) { $totalarray['totaldurationfield'] = $totalarray['nbfield']; } if (empty($totalarray['totalduration'])) { - $totalarray['totalduration'] = $task_time->task_duration; + $totalarray['totalduration'] = $task_time->element_duration; } else { - $totalarray['totalduration'] += $task_time->task_duration; + $totalarray['totalduration'] += $task_time->element_duration; } } @@ -2308,7 +2309,7 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser // Value spent if (!empty($arrayfields['value']['checked'])) { $langs->load("salaries"); - $value = price2num($task_time->thm * $task_time->task_duration / 3600, 'MT', 1); + $value = price2num($task_time->thm * $task_time->element_duration / 3600, 'MT', 1); print ''; print 'thm).'">'; @@ -2386,11 +2387,11 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser print ''; } - print 'rowid.$param.((empty($id) || $tab == 'timespent') ? '&tab=timespent' : '').'">'; + print 'rowid.$param.((empty($id) || $tab == 'timespent') ? '&tab=timespent' : '').'">'; print img_edit('default', 0, 'class="pictofixedwidth paddingleft"'); print ''; - print 'rowid.$param.((empty($id) || $tab == 'timespent') ? '&tab=timespent' : '').'">'; + print 'rowid.$param.((empty($id) || $tab == 'timespent') ? '&tab=timespent' : '').'">'; print img_delete('default', 'class="pictodelete paddingleft"'); print ''; @@ -2419,16 +2420,16 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser print ''; // Date - if (!empty($arrayfields['t.task_date']['checked'])) { + if (!empty($arrayfields['t.element_date']['checked'])) { print ''; if ($action == 'splitline' && GETPOST('lineid', 'int') == $task_time->rowid) { - if (empty($task_time->task_date_withhour)) { + if (empty($task_time->element_date_withhour)) { print $form->selectDate(($date2 ? $date2 : $date1), 'timeline', 3, 3, 2, "timespent_date", 1, 0); } else { print $form->selectDate(($date2 ? $date2 : $date1), 'timeline', 1, 1, 2, "timespent_date", 1, 0); } } else { - print dol_print_date(($date2 ? $date2 : $date1), ($task_time->task_date_withhour ? 'dayhour' : 'day')); + print dol_print_date(($date2 ? $date2 : $date1), ($task_time->element_date_withhour ? 'dayhour' : 'day')); } print ''; } @@ -2442,10 +2443,10 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser } // Task ref - if (!empty($arrayfields['t.task_ref']['checked'])) { + if (!empty($arrayfields['t.element_ref']['checked'])) { if ((empty($id) && empty($ref)) || !empty($projectidforalltimes)) { // Not a dedicated task print ''; - $tasktmp->id = $task_time->fk_task; + $tasktmp->id = $task_time->fk_element; $tasktmp->ref = $task_time->ref; $tasktmp->label = $task_time->label; print $tasktmp->getNomUrl(1, 'withproject', 'time'); @@ -2454,7 +2455,7 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser } // Task label - if (!empty($arrayfields['t.task_label']['checked'])) { + if (!empty($arrayfields['t.element_label']['checked'])) { if ((empty($id) && empty($ref)) || !empty($projectidforalltimes)) { // Not a dedicated task print ''; print dol_escape_htmltag($task_time->label); @@ -2504,13 +2505,13 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser } // Time spent - if (!empty($arrayfields['t.task_duration']['checked'])) { + if (!empty($arrayfields['t.element_duration']['checked'])) { print ''; if ($action == 'splitline' && GETPOST('lineid', 'int') == $task_time->rowid) { - print ''; - print $form->select_duration('new_duration', $task_time->task_duration, 0, 'text'); + print ''; + print $form->select_duration('new_duration', $task_time->element_duration, 0, 'text'); } else { - print convertSecondToTime($task_time->task_duration, 'allhourmin'); + print convertSecondToTime($task_time->element_duration, 'allhourmin'); } print ''; } @@ -2519,7 +2520,7 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser if (!empty($arrayfields['value']['checked'])) { print ''; print ''; - $value = price2num($task_time->thm * $task_time->task_duration / 3600, 'MT', 1); + $value = price2num($task_time->thm * $task_time->element_duration / 3600, 'MT', 1); print price($value, 1, $langs, 1, -1, -1, $conf->currency); print ''; print ''; @@ -2557,16 +2558,16 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser print ''; // Date - if (!empty($arrayfields['t.task_date']['checked'])) { + if (!empty($arrayfields['t.element_date']['checked'])) { print ''; if ($action == 'splitline' && GETPOST('lineid', 'int') == $task_time->rowid) { - if (empty($task_time->task_date_withhour)) { + if (empty($task_time->element_date_withhour)) { print $form->selectDate(($date2 ? $date2 : $date1), 'timeline_2', 3, 3, 2, "timespent_date", 1, 0); } else { print $form->selectDate(($date2 ? $date2 : $date1), 'timeline_2', 1, 1, 2, "timespent_date", 1, 0); } } else { - print dol_print_date(($date2 ? $date2 : $date1), ($task_time->task_date_withhour ? 'dayhour' : 'day')); + print dol_print_date(($date2 ? $date2 : $date1), ($task_time->element_date_withhour ? 'dayhour' : 'day')); } print ''; } @@ -2580,10 +2581,10 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser } // Task ref - if (!empty($arrayfields['t.task_ref']['checked'])) { + if (!empty($arrayfields['t.element_ref']['checked'])) { if ((empty($id) && empty($ref)) || !empty($projectidforalltimes)) { // Not a dedicated task print ''; - $tasktmp->id = $task_time->fk_task; + $tasktmp->id = $task_time->fk_element; $tasktmp->ref = $task_time->ref; $tasktmp->label = $task_time->label; print $tasktmp->getNomUrl(1, 'withproject', 'time'); @@ -2592,7 +2593,7 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser } // Task label - if (!empty($arrayfields['t.task_label']['checked'])) { + if (!empty($arrayfields['t.element_label']['checked'])) { if ((empty($id) && empty($ref)) || !empty($projectidforalltimes)) { // Not a dedicated task print ''; print $task_time->label; @@ -2642,13 +2643,13 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser } // Time spent - if (!empty($arrayfields['t.task_duration']['checked'])) { + if (!empty($arrayfields['t.element_duration']['checked'])) { print ''; if ($action == 'splitline' && GETPOST('lineid', 'int') == $task_time->rowid) { print ''; print $form->select_duration('new_duration_2', 0, 0, 'text'); } else { - print convertSecondToTime($task_time->task_duration, 'allhourmin'); + print convertSecondToTime($task_time->element_duration, 'allhourmin'); } print ''; }