diff --git a/htdocs/install/mysql/migration/15.0.0-16.0.0.sql b/htdocs/install/mysql/migration/15.0.0-16.0.0.sql new file mode 100644 index 00000000000..64df9ca79cb --- /dev/null +++ b/htdocs/install/mysql/migration/15.0.0-16.0.0.sql @@ -0,0 +1,34 @@ +-- +-- Be carefull to requests order. +-- This file must be loaded by calling /install/index.php page +-- when current version is 14.0.0 or higher. +-- +-- To restrict request to Mysql version x.y minimum use -- VMYSQLx.y +-- To restrict request to Pgsql version x.y minimum use -- VPGSQLx.y +-- To rename a table: ALTER TABLE llx_table RENAME TO llx_table_new; +-- To add a column: ALTER TABLE llx_table ADD COLUMN newcol varchar(60) NOT NULL DEFAULT '0' AFTER existingcol; +-- To rename a column: ALTER TABLE llx_table CHANGE COLUMN oldname newname varchar(60); +-- To drop a column: ALTER TABLE llx_table DROP COLUMN oldname; +-- To change type of field: ALTER TABLE llx_table MODIFY COLUMN name varchar(60); +-- To drop a foreign key: ALTER TABLE llx_table DROP FOREIGN KEY fk_name; +-- To create a unique index ALTER TABLE llx_table ADD UNIQUE INDEX uk_table_field (field); +-- To drop an index: -- VMYSQL4.1 DROP INDEX nomindex on llx_table; +-- To drop an index: -- VPGSQL8.2 DROP INDEX nomindex; +-- To make pk to be auto increment (mysql): -- VMYSQL4.3 ALTER TABLE llx_table CHANGE COLUMN rowid rowid INTEGER NOT NULL AUTO_INCREMENT; +-- To make pk to be auto increment (postgres): +-- -- VPGSQL8.2 CREATE SEQUENCE llx_table_rowid_seq OWNED BY llx_table.rowid; +-- -- VPGSQL8.2 ALTER TABLE llx_table ADD PRIMARY KEY (rowid); +-- -- VPGSQL8.2 ALTER TABLE llx_table ALTER COLUMN rowid SET DEFAULT nextval('llx_table_rowid_seq'); +-- -- VPGSQL8.2 SELECT setval('llx_table_rowid_seq', MAX(rowid)) FROM llx_table; +-- To set a field as NULL: -- VMYSQL4.3 ALTER TABLE llx_table MODIFY COLUMN name varchar(60) NULL; +-- To set a field as NULL: -- VPGSQL8.2 ALTER TABLE llx_table ALTER COLUMN name DROP NOT NULL; +-- To set a field as NOT NULL: -- VMYSQL4.3 ALTER TABLE llx_table MODIFY COLUMN name varchar(60) NOT NULL; +-- To set a field as NOT NULL: -- VPGSQL8.2 ALTER TABLE llx_table ALTER COLUMN name SET NOT NULL; +-- To set a field as default NULL: -- VPGSQL8.2 ALTER TABLE llx_table ALTER COLUMN name SET DEFAULT NULL; +-- Note: fields with type BLOB/TEXT can't have default value. +-- To rebuild sequence for postgresql after insert by forcing id autoincrement fields: +-- -- VPGSQL8.2 SELECT dol_util_rebuild_sequences(); + + +-- Missing in v14 or lower +ALTER TABLE llx_projet_task_time ADD COLUMN fk_product integer NULL; diff --git a/htdocs/install/mysql/tables/llx_projet_task_time.sql b/htdocs/install/mysql/tables/llx_projet_task_time.sql index 786d8907588..63eadb1177f 100644 --- a/htdocs/install/mysql/tables/llx_projet_task_time.sql +++ b/htdocs/install/mysql/tables/llx_projet_task_time.sql @@ -24,6 +24,7 @@ create table llx_projet_task_time task_datehour datetime, -- day + hour task_date_withhour integer DEFAULT 0, -- 0 by default, 1 if date was entered with start hour task_duration double, + fk_product integer NULL, fk_user integer, thm double(24,8), invoice_id integer DEFAULT NULL, -- If we need to invoice each line of timespent, we can save invoice id here diff --git a/htdocs/projet/class/task.class.php b/htdocs/projet/class/task.class.php index dac2afa4834..5a6a3e1fba0 100644 --- a/htdocs/projet/class/task.class.php +++ b/htdocs/projet/class/task.class.php @@ -118,6 +118,7 @@ class Task extends CommonObject public $timespent_fk_user; public $timespent_thm; public $timespent_note; + public $timespent_fk_product; public $comments = array(); @@ -1201,6 +1202,7 @@ class Task extends CommonObject $sql .= ", task_date_withhour"; $sql .= ", task_duration"; $sql .= ", fk_user"; + $sql .= ", fk_product"; $sql .= ", note"; $sql .= ") VALUES ("; $sql .= ((int) $this->id); @@ -1209,6 +1211,7 @@ class Task extends CommonObject $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 .= ")"; @@ -1404,6 +1407,7 @@ class Task extends CommonObject $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"; @@ -1422,6 +1426,7 @@ class Task extends CommonObject $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; } @@ -1575,6 +1580,7 @@ class Task extends CommonObject $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); diff --git a/htdocs/projet/tasks/time.php b/htdocs/projet/tasks/time.php index f9f3a2aca43..03022cfd1c3 100644 --- a/htdocs/projet/tasks/time.php +++ b/htdocs/projet/tasks/time.php @@ -73,6 +73,7 @@ $search_task_ref = GETPOST('search_task_ref', 'alpha'); $search_task_label = GETPOST('search_task_label', 'alpha'); $search_user = GETPOST('search_user', 'int'); $search_valuebilled = GETPOST('search_valuebilled', 'int'); +$search_product_ref = GETPOST('search_product_ref', 'alpha'); // Security check $socid = 0; @@ -161,6 +162,7 @@ if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter.x' $search_task_label = ''; $search_user = 0; $search_valuebilled = ''; + $search_product_ref = ''; $toselect = ''; $search_array_options = array(); $action = ''; @@ -215,6 +217,7 @@ if ($action == 'addtimespent' && $user->rights->projet->lire) { $object->timespent_date = dol_mktime(12, 0, 0, GETPOST("timemonth", 'int'), GETPOST("timeday", 'int'), GETPOST("timeyear", 'int')); } $object->timespent_fk_user = GETPOST("userid", 'int'); + $object->timespent_fk_product = GETPOST("fk_product", 'int'); $result = $object->addTimeSpent($user); if ($result >= 0) { setEventMessages($langs->trans("RecordSaved"), null, 'mesgs'); @@ -261,6 +264,7 @@ if (($action == 'updateline' || $action == 'updatesplitline') && !$cancel && $us $object->timespent_date = dol_mktime(12, 0, 0, GETPOST("timelinemonth"), GETPOST("timelineday"), GETPOST("timelineyear")); } $object->timespent_fk_user = GETPOST("userid_line", 'int'); + $object->timespent_fk_product = GETPOST("fk_product", 'int'); $result = $object->addTimeSpent($user); if ($result >= 0) { setEventMessages($langs->trans("RecordSaved"), null, 'mesgs'); @@ -284,6 +288,7 @@ if (($action == 'updateline' || $action == 'updatesplitline') && !$cancel && $us $object->timespent_date = dol_mktime(12, 0, 0, GETPOST("timelinemonth", 'int'), GETPOST("timelineday", 'int'), GETPOST("timelineyear", 'int')); } $object->timespent_fk_user = GETPOST("userid_line", 'int'); + $object->timespent_fk_product = GETPOST("fk_product", 'int'); $result = $object->updateTimeSpent($user); if ($result >= 0) { @@ -367,6 +372,7 @@ if ($action == 'confirm_generateinvoice') { $fuser = new User($db); $db->begin(); + //TODO produit du temps passé ou produt id $idprod = GETPOST('productid', 'int'); $generateinvoicemode = GETPOST('generateinvoicemode', 'string'); $invoiceToUse = GETPOST('invoiceid', 'int'); @@ -1029,6 +1035,9 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser } $arrayfields['author'] = array('label'=>$langs->trans("By"), 'checked'=>1); $arrayfields['t.note'] = array('label'=>$langs->trans("Note"), 'checked'=>1); + if ($conf->service->enabled && $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['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)); @@ -1167,6 +1176,7 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser $form->select_produits('', 'productid', '1', 0, $projectstatic->thirdparty->price_level, 1, 2, '', 0, array(), $projectstatic->thirdparty->id, 'None', 0, 'maxwidth500'); print ''; print ''; + //TODO : Use product of time affect } print ''; @@ -1238,12 +1248,14 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser $tasks = array(); $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 .= " 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,"; $sql .= " il.fk_facture as invoice_id, inv.fk_statut"; $sql .= " FROM ".MAIN_DB_PREFIX."projet_task_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."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 .= " ".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"; @@ -1273,6 +1285,9 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser if ($search_user > 0) { $sql .= natural_search('t.fk_user', $search_user, 2); } + if (!empty($search_product_ref)) { + $sql .= natural_search('prod.ref', $search_product_ref); + } if ($search_valuebilled == '1') { $sql .= ' AND t.invoice_id > 0'; } @@ -1365,6 +1380,10 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser print ''.$langs->trans("ProgressDeclared").''; if (empty($conf->global->PROJECT_HIDE_TASKS) && !empty($conf->global->PROJECT_BILL_TIME_SPENT)) { print ''; + + if ($conf->service->enabled && $projectstatic->thirdparty->id > 0 && $projectstatic->usage_bill_time) { + print ''.$langs->trans("Product").''; + } } print ''; print "\n"; @@ -1436,6 +1455,12 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser if (empty($conf->global->PROJECT_HIDE_TASKS) && !empty($conf->global->PROJECT_BILL_TIME_SPENT)) { print ''; print ''; + + if ($conf->service->enabled && $projectstatic->thirdparty->id > 0 && $projectstatic->usage_bill_time) { + print ''; + print $form->select_produits('', 'fk_product', '1', 0, $projectstatic->thirdparty->price_level, 1, 2, '', 0, array(), $projectstatic->thirdparty->id, 'None', 0, 'maxwidth500'); + print ''; + } } print ''; @@ -1509,6 +1534,10 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser if (!empty($arrayfields['t.task_duration']['checked'])) { print ''; } + // Product + if (!empty($arrayfields['t.fk_product']['checked'])) { + print ''; + } // Value in main currency if (!empty($arrayfields['value']['checked'])) { print ''; @@ -1557,6 +1586,10 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser 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.fk_product']['checked'])) { + print_liste_field_titre($arrayfields['t.fk_product']['label'], $_SERVER['PHP_SELF'], 't.fk_product', '', $param, '', $sortfield, $sortorder, 'right '); + } + if (!empty($arrayfields['value']['checked'])) { print_liste_field_titre($arrayfields['value']['label'], $_SERVER['PHP_SELF'], '', '', $param, '', $sortfield, $sortorder, 'right '); } @@ -1732,6 +1765,24 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser $totalarray['totalduration'] += $task_time->task_duration; } + //Product + if (!empty($arrayfields['t.fk_product']['checked'])) { + print ''; + if ($action == 'editline' && $_GET['lineid'] == $task_time->rowid) { + $form->select_produits($task_time->fk_product, 'fk_product', '1', 0, $projectstatic->thirdparty->price_level, 1, 2, '', 0, array(), $projectstatic->thirdparty->id, 'None', 0, 'maxwidth500'); + } elseif (!empty($task_time->fk_product)) { + $product = new Product($db); + $resultFetch = $product->fetch($task_time->fk_product); + if ($resultFetch < 0) { + setEventMessages($product->error, $product->errors, 'errors'); + } else { + + print $product->getNomUrl(1); + } + } + print ''; + } + // Value spent if (!empty($arrayfields['value']['checked'])) { $langs->load("salaries");