Merge pull request #19765 from FHenry/dev_19763
NEW: Can invoice task time per different services
This commit is contained in:
commit
d97b882cf8
@ -3820,6 +3820,7 @@ class Facture extends CommonInvoice
|
||||
return -2;
|
||||
}
|
||||
} else {
|
||||
$this->errors[]='status of invoice must be Draft to allow use of ->addline()';
|
||||
dol_syslog(get_class($this)."::addline status of invoice must be Draft to allow use of ->addline()", LOG_ERR);
|
||||
return -3;
|
||||
}
|
||||
|
||||
@ -259,7 +259,7 @@ TimeSpentInvoiced=Time spent billed
|
||||
TimeSpentForIntervention=Time spent
|
||||
TimeSpentForInvoice=Time spent
|
||||
OneLinePerUser=One line per user
|
||||
ServiceToUseOnLines=Service to use on lines
|
||||
ServiceToUseOnLines=Service to use on lines by default
|
||||
InvoiceGeneratedFromTimeSpent=Invoice %s has been generated from time spent on project
|
||||
InterventionGeneratedFromTimeSpent=Intervention %s has been generated from time spent on project
|
||||
ProjectBillTimeDescription=Check if you enter timesheet on tasks of project AND you plan to generate invoice(s) from the timesheet to bill the customer of the project (do not check if you plan to create invoice that is not based on entered timesheets). Note: To generate invoice, go on tab 'Time spent' of the project and select lines to include.
|
||||
|
||||
@ -6159,6 +6159,43 @@ class Product extends CommonObject
|
||||
dol_print_error($this->db);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the duration in Hours of a service base on duration fields
|
||||
* @return int -1 KO, >= 0 is the duration in hours
|
||||
*/
|
||||
public function getProductDurationHours()
|
||||
{
|
||||
global $langs;
|
||||
|
||||
if (empty($this->duration_value)) {
|
||||
$this->errors[]='ErrorDurationForServiceNotDefinedCantCalculateHourlyPrice';
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ($this->duration_unit == 'i') {
|
||||
$prodDurationHours = 1. / 60;
|
||||
}
|
||||
if ($this->duration_unit == 'h') {
|
||||
$prodDurationHours = 1.;
|
||||
}
|
||||
if ($this->duration_unit == 'd') {
|
||||
$prodDurationHours = 24.;
|
||||
}
|
||||
if ($this->duration_unit == 'w') {
|
||||
$prodDurationHours = 24. * 7;
|
||||
}
|
||||
if ($this->duration_unit == 'm') {
|
||||
$prodDurationHours = 24. * 30;
|
||||
}
|
||||
if ($this->duration_unit == 'y') {
|
||||
$prodDurationHours = 24. * 365;
|
||||
}
|
||||
$prodDurationHours *= $this->duration_value;
|
||||
|
||||
return $prodDurationHours;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -127,6 +127,7 @@ class Task extends CommonObjectLine
|
||||
public $timespent_fk_user;
|
||||
public $timespent_thm;
|
||||
public $timespent_note;
|
||||
public $timespent_fk_product;
|
||||
|
||||
public $comments = array();
|
||||
|
||||
@ -1227,6 +1228,7 @@ class Task extends CommonObjectLine
|
||||
$sql .= ", task_date_withhour";
|
||||
$sql .= ", task_duration";
|
||||
$sql .= ", fk_user";
|
||||
$sql .= ", fk_product";
|
||||
$sql .= ", note";
|
||||
$sql .= ", datec";
|
||||
$sql .= ") VALUES (";
|
||||
@ -1236,6 +1238,7 @@ class Task extends CommonObjectLine
|
||||
$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 .= ")";
|
||||
@ -1523,6 +1526,7 @@ class Task extends CommonObjectLine
|
||||
$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";
|
||||
@ -1541,6 +1545,7 @@ class Task extends CommonObjectLine
|
||||
$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;
|
||||
}
|
||||
@ -1694,6 +1699,7 @@ class Task extends CommonObjectLine
|
||||
$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);
|
||||
|
||||
|
||||
@ -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');
|
||||
$search_company = GETPOST('$search_company', 'alpha');
|
||||
|
||||
$limit = GETPOST('limit', 'int') ?GETPOST('limit', 'int') : $conf->liste_limit;
|
||||
@ -166,6 +167,7 @@ if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter.x'
|
||||
$search_task_label = '';
|
||||
$search_user = 0;
|
||||
$search_valuebilled = '';
|
||||
$search_product_ref = '';
|
||||
$toselect = array();
|
||||
$search_array_options = array();
|
||||
$action = '';
|
||||
@ -220,6 +222,7 @@ if ($action == 'addtimespent' && $user->rights->projet->time) {
|
||||
$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');
|
||||
@ -271,17 +274,17 @@ 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 = 0;
|
||||
if (in_array($object->timespent_fk_user, $childids) || $user->rights->projet->all->creer) {
|
||||
$result = $object->addTimeSpent($user);
|
||||
}
|
||||
|
||||
if ($result >= 0) {
|
||||
setEventMessages($langs->trans("RecordSaved"), null, 'mesgs');
|
||||
} else {
|
||||
setEventMessages($langs->trans($object->error), null, 'errors');
|
||||
$error++;
|
||||
if ($result >= 0) {
|
||||
setEventMessages($langs->trans("RecordSaved"), null, 'mesgs');
|
||||
} else {
|
||||
setEventMessages($langs->trans($object->error), null, 'errors');
|
||||
$error++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$object->fetch($id, $ref);
|
||||
@ -298,6 +301,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 = 0;
|
||||
if (in_array($object->timespent_fk_user, $childids) || $user->rights->projet->all->creer) {
|
||||
@ -391,36 +395,22 @@ if ($action == 'confirm_generateinvoice') {
|
||||
$generateinvoicemode = GETPOST('generateinvoicemode', 'string');
|
||||
$invoiceToUse = GETPOST('invoiceid', 'int');
|
||||
|
||||
$prodDurationHours = 1.0;
|
||||
$prodDurationHoursBase = 1.0;
|
||||
$product_data_cache = array();
|
||||
if ($idprod > 0) {
|
||||
$tmpproduct->fetch($idprod);
|
||||
if ($result<0) {
|
||||
$error++;
|
||||
setEventMessages($tmpproduct->error, $tmpproduct->errors, 'errors');
|
||||
}
|
||||
|
||||
if (empty($tmpproduct->duration_value)) {
|
||||
$prodDurationHoursBase=$tmpproduct->getProductDurationHours();
|
||||
if ($prodDurationHoursBase<0) {
|
||||
$error++;
|
||||
$langs->load("errors");
|
||||
setEventMessages($langs->trans("ErrorDurationForServiceNotDefinedCantCalculateHourlyPrice"), null, 'errors');
|
||||
setEventMessages(null, $tmpproduct->errors, 'errors');
|
||||
}
|
||||
|
||||
if ($tmpproduct->duration_unit == 'i') {
|
||||
$prodDurationHours = 1. / 60;
|
||||
}
|
||||
if ($tmpproduct->duration_unit == 'h') {
|
||||
$prodDurationHours = 1.;
|
||||
}
|
||||
if ($tmpproduct->duration_unit == 'd') {
|
||||
$prodDurationHours = 24.;
|
||||
}
|
||||
if ($tmpproduct->duration_unit == 'w') {
|
||||
$prodDurationHours = 24. * 7;
|
||||
}
|
||||
if ($tmpproduct->duration_unit == 'm') {
|
||||
$prodDurationHours = 24. * 30;
|
||||
}
|
||||
if ($tmpproduct->duration_unit == 'y') {
|
||||
$prodDurationHours = 24. * 365;
|
||||
}
|
||||
$prodDurationHours *= $tmpproduct->duration_value;
|
||||
|
||||
$dataforprice = $tmpproduct->getSellPrice($mysoc, $projectstatic->thirdparty, 0);
|
||||
|
||||
$pu_ht = empty($dataforprice['pu_ht']) ? 0 : $dataforprice['pu_ht'];
|
||||
@ -428,7 +418,7 @@ if ($action == 'confirm_generateinvoice') {
|
||||
$localtax1 = $dataforprice['localtax1'];
|
||||
$localtax2 = $dataforprice['localtax2'];
|
||||
} else {
|
||||
$prodDurationHours = 1;
|
||||
$prodDurationHoursBase = 1;
|
||||
|
||||
$pu_ht = 0;
|
||||
$txtva = get_default_tva($mysoc, $projectstatic->thirdparty);
|
||||
@ -456,39 +446,85 @@ if ($action == 'confirm_generateinvoice') {
|
||||
foreach ($toselect as $key => $value) {
|
||||
// Get userid, timepent
|
||||
$object->fetchTimeSpent($value);
|
||||
$arrayoftasks[$object->timespent_fk_user]['timespent'] += $object->timespent_duration;
|
||||
$arrayoftasks[$object->timespent_fk_user]['totalvaluetodivideby3600'] += ($object->timespent_duration * $object->timespent_thm);
|
||||
$arrayoftasks[$object->timespent_fk_user][(int) $object->timespent_fk_product]['timespent'] += $object->timespent_duration;
|
||||
$arrayoftasks[$object->timespent_fk_user][(int) $object->timespent_fk_product]['totalvaluetodivideby3600'] += ($object->timespent_duration * $object->timespent_thm);
|
||||
}
|
||||
|
||||
foreach ($arrayoftasks as $userid => $value) {
|
||||
foreach ($arrayoftasks as $userid => $data) {
|
||||
$fuser->fetch($userid);
|
||||
//$pu_ht = $value['timespent'] * $fuser->thm;
|
||||
$username = $fuser->getFullName($langs);
|
||||
foreach ($data as $fk_product=>$timespent_data) {
|
||||
// Define qty per hour
|
||||
$qtyhour = $timespent_data['timespent'] / 3600;
|
||||
$qtyhourtext = convertSecondToTime($timespent_data['timespent'], 'all', $conf->global->MAIN_DURATION_OF_WORKDAY);
|
||||
|
||||
// Define qty per hour
|
||||
$qtyhour = $value['timespent'] / 3600;
|
||||
$qtyhourtext = convertSecondToTime($value['timespent'], 'all', $conf->global->MAIN_DURATION_OF_WORKDAY);
|
||||
// If no unit price known
|
||||
if (empty($pu_ht)) {
|
||||
$pu_ht = price2num($timespent_data['totalvaluetodivideby3600'] / 3600, 'MU');
|
||||
}
|
||||
|
||||
// If no unit price known
|
||||
if (empty($pu_ht)) {
|
||||
$pu_ht = price2num($value['totalvaluetodivideby3600'] / 3600, 'MU');
|
||||
}
|
||||
// Add lines
|
||||
$prodDurationHours = $prodDurationHoursBase;
|
||||
$idprodline=$idprod;
|
||||
$pu_htline = $pu_ht;
|
||||
$txtvaline = $txtva;
|
||||
$localtax1line = $localtax1;
|
||||
$localtax2line = $localtax2;
|
||||
|
||||
// Add lines
|
||||
$lineid = $tmpinvoice->addline($langs->trans("TimeSpentForInvoice", $username).' : '.$qtyhourtext, $pu_ht, round($qtyhour / $prodDurationHours, 2), $txtva, $localtax1, $localtax2, ($idprod > 0 ? $idprod : 0));
|
||||
if (!empty($fk_product) && $fk_product!==$idprod) {
|
||||
if (!array_key_exists($fk_product, $product_data_cache)) {
|
||||
$result = $tmpproduct->fetch($fk_product);
|
||||
if ($result < 0) {
|
||||
$error++;
|
||||
setEventMessages($tmpproduct->error, $tmpproduct->errors, 'errors');
|
||||
}
|
||||
$prodDurationHours = $tmpproduct->getProductDurationHours();
|
||||
if ($prodDurationHours < 0) {
|
||||
$error++;
|
||||
$langs->load("errors");
|
||||
setEventMessages(null, $tmpproduct->errors, 'errors');
|
||||
}
|
||||
|
||||
// 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 .= ' WHERE rowid IN ('.$db->sanitize(join(',', $toselect)).') AND fk_user = '.((int) $userid);
|
||||
$result = $db->query($sql);
|
||||
if (!$result) {
|
||||
$error++;
|
||||
setEventMessages($db->lasterror(), null, 'errors');
|
||||
break;
|
||||
$dataforprice = $tmpproduct->getSellPrice($mysoc, $projectstatic->thirdparty, 0);
|
||||
|
||||
$pu_htline = empty($dataforprice['pu_ht']) ? 0 : $dataforprice['pu_ht'];
|
||||
$txtvaline = $dataforprice['tva_tx'];
|
||||
$localtax1line = $dataforprice['localtax1'];
|
||||
$localtax2line = $dataforprice['localtax2'];
|
||||
|
||||
$product_data_cache[$fk_product] = array('duration'=>$prodDurationHours,'dataforprice'=>$dataforprice);
|
||||
} else {
|
||||
$prodDurationHours = $product_data_cache[$fk_product]['duration'];
|
||||
$pu_htline = empty($product_data_cache[$fk_product]['dataforprice']['pu_ht']) ? 0 : $product_data_cache[$fk_product]['dataforprice']['pu_ht'];
|
||||
$txtvaline = $product_data_cache[$fk_product]['dataforprice']['tva_tx'];
|
||||
$localtax1line = $product_data_cache[$fk_product]['dataforprice']['localtax1'];
|
||||
$localtax2line = $product_data_cache[$fk_product]['dataforprice']['localtax2'];
|
||||
}
|
||||
$idprodline=$fk_product;
|
||||
}
|
||||
|
||||
// Add lines
|
||||
$lineid = $tmpinvoice->addline($langs->trans("TimeSpentForInvoice", $username).' : '.$qtyhourtext, $pu_htline, round($qtyhour / $prodDurationHours, 2), $txtvaline, $localtax1line, $localtax2line, ($idprodline > 0 ? $idprodline : 0));
|
||||
if ($lineid<0) {
|
||||
$error++;
|
||||
setEventMessages(null, $tmpinvoice->errors, 'errors');
|
||||
}
|
||||
|
||||
// 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 .= ' WHERE rowid IN ('.$db->sanitize(join(',', $toselect)).') AND fk_user = '.((int) $userid);
|
||||
$result = $db->query($sql);
|
||||
if (!$result) {
|
||||
$error++;
|
||||
setEventMessages($db->lasterror(), null, 'errors');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} elseif ($generateinvoicemode == 'onelineperperiod') { // One line for each time spent line
|
||||
$arrayoftasks = array();
|
||||
|
||||
$withdetail=GETPOST('detail_time_duration', 'alpha');
|
||||
foreach ($toselect as $key => $value) {
|
||||
// Get userid, timepent
|
||||
@ -518,6 +554,7 @@ if ($action == 'confirm_generateinvoice') {
|
||||
$arrayoftasks[$object->timespent_id]['note'] .= ' - '.$langs->trans("Duration").': '.convertSecondToTime($object->timespent_duration, 'all', $conf->global->MAIN_DURATION_OF_WORKDAY);
|
||||
}
|
||||
$arrayoftasks[$object->timespent_id]['user'] = $object->timespent_fk_user;
|
||||
$arrayoftasks[$object->timespent_id]['fk_product'] = $object->timespent_fk_product;
|
||||
}
|
||||
|
||||
foreach ($arrayoftasks as $timespent_id => $value) {
|
||||
@ -533,7 +570,49 @@ if ($action == 'confirm_generateinvoice') {
|
||||
}
|
||||
|
||||
// Add lines
|
||||
$lineid = $tmpinvoice->addline($value['note'], $pu_ht, round($qtyhour / $prodDurationHours, 2), $txtva, $localtax1, $localtax2, ($idprod > 0 ? $idprod : 0));
|
||||
$prodDurationHours = $prodDurationHoursBase;
|
||||
$idprodline=$idprod;
|
||||
$pu_htline = $pu_ht;
|
||||
$txtvaline = $txtva;
|
||||
$localtax1line = $localtax1;
|
||||
$localtax2line = $localtax2;
|
||||
|
||||
if (!empty($value['fk_product']) && $value['fk_product']!==$idprod) {
|
||||
if (!array_key_exists($value['fk_product'], $product_data_cache)) {
|
||||
$result = $tmpproduct->fetch($value['fk_product']);
|
||||
if ($result < 0) {
|
||||
$error++;
|
||||
setEventMessages($tmpproduct->error, $tmpproduct->errors, 'errors');
|
||||
}
|
||||
$prodDurationHours = $tmpproduct->getProductDurationHours();
|
||||
if ($prodDurationHours < 0) {
|
||||
$error++;
|
||||
$langs->load("errors");
|
||||
setEventMessages(null, $tmpproduct->errors, 'errors');
|
||||
}
|
||||
|
||||
$dataforprice = $tmpproduct->getSellPrice($mysoc, $projectstatic->thirdparty, 0);
|
||||
|
||||
$pu_htline = empty($dataforprice['pu_ht']) ? 0 : $dataforprice['pu_ht'];
|
||||
$txtvaline = $dataforprice['tva_tx'];
|
||||
$localtax1line = $dataforprice['localtax1'];
|
||||
$localtax2line = $dataforprice['localtax2'];
|
||||
|
||||
$product_data_cache[$value['fk_product']] = array('duration'=>$prodDurationHours,'dataforprice'=>$dataforprice);
|
||||
} else {
|
||||
$prodDurationHours = $product_data_cache[$value['fk_product']]['duration'];
|
||||
$pu_htline = empty($product_data_cache[$value['fk_product']]['dataforprice']['pu_ht']) ? 0 : $product_data_cache[$value['fk_product']]['dataforprice']['pu_ht'];
|
||||
$txtvaline = $product_data_cache[$value['fk_product']]['dataforprice']['tva_tx'];
|
||||
$localtax1line = $product_data_cache[$value['fk_product']]['dataforprice']['localtax1'];
|
||||
$localtax2line = $product_data_cache[$value['fk_product']]['dataforprice']['localtax2'];
|
||||
}
|
||||
$idprodline=$value['fk_product'];
|
||||
}
|
||||
$lineid = $tmpinvoice->addline($value['note'], $pu_htline, round($qtyhour / $prodDurationHours, 2), $txtvaline, $localtax1line, $localtax2line, ($idprodline > 0 ? $idprodline : 0));
|
||||
if ($lineid<0) {
|
||||
$error++;
|
||||
setEventMessages(null, $tmpinvoice->errors, 'errors');
|
||||
}
|
||||
//var_dump($lineid);exit;
|
||||
|
||||
// Update lineid into line of timespent
|
||||
@ -552,56 +631,99 @@ if ($action == 'confirm_generateinvoice') {
|
||||
// Get userid, timepent
|
||||
$object->fetchTimeSpent($value); // Call method to get list of timespent for a timespent line id (We use the utiliy method found into Task object)
|
||||
// $object->id is now the task id
|
||||
$arrayoftasks[$object->id]['timespent'] += $object->timespent_duration;
|
||||
$arrayoftasks[$object->id]['totalvaluetodivideby3600'] += ($object->timespent_duration * $object->timespent_thm);
|
||||
$arrayoftasks[$object->id][(int) $object->timespent_fk_product]['timespent'] += $object->timespent_duration;
|
||||
$arrayoftasks[$object->id][(int) $object->timespent_fk_product]['totalvaluetodivideby3600'] += ($object->timespent_duration * $object->timespent_thm);
|
||||
}
|
||||
|
||||
foreach ($arrayoftasks as $task_id => $value) {
|
||||
foreach ($arrayoftasks as $task_id => $data) {
|
||||
$ftask = new Task($db);
|
||||
$ftask->fetch($task_id);
|
||||
// Define qty per hour
|
||||
$qtyhour = $value['timespent'] / 3600;
|
||||
$qtyhourtext = convertSecondToTime($value['timespent'], 'all', $conf->global->MAIN_DURATION_OF_WORKDAY);
|
||||
|
||||
if ($idprod > 0) {
|
||||
// If a product is defined, we msut use the $prodDurationHours and $pu_ht of product (already set previously).
|
||||
$pu_ht_for_task = $pu_ht;
|
||||
// If we want to reuse the value of timespent (so use same price than cost price)
|
||||
if (!empty($conf->global->PROJECT_TIME_SPENT_INTO_INVOICE_USE_VALUE)) {
|
||||
$pu_ht_for_task = price2num($value['totalvaluetodivideby3600'] / $value['timespent'], 'MU') * $prodDurationHours;
|
||||
foreach ($data as $fk_product=>$timespent_data) {
|
||||
$qtyhour = $timespent_data['timespent'] / 3600;
|
||||
$qtyhourtext = convertSecondToTime($timespent_data['timespent'], 'all', $conf->global->MAIN_DURATION_OF_WORKDAY);
|
||||
|
||||
// Add lines
|
||||
$prodDurationHours = $prodDurationHoursBase;
|
||||
$idprodline=$idprod;
|
||||
$pu_htline = $pu_ht;
|
||||
$txtvaline = $txtva;
|
||||
$localtax1line = $localtax1;
|
||||
$localtax2line = $localtax2;
|
||||
|
||||
if (!empty($fk_product) && $fk_product!==$idprod) {
|
||||
if (!array_key_exists($fk_product, $product_data_cache)) {
|
||||
$result = $tmpproduct->fetch($fk_product);
|
||||
if ($result < 0) {
|
||||
$error++;
|
||||
setEventMessages($tmpproduct->error, $tmpproduct->errors, 'errors');
|
||||
}
|
||||
$prodDurationHours = $tmpproduct->getProductDurationHours();
|
||||
if ($prodDurationHours < 0) {
|
||||
$error++;
|
||||
$langs->load("errors");
|
||||
setEventMessages(null, $tmpproduct->errors, 'errors');
|
||||
}
|
||||
|
||||
$dataforprice = $tmpproduct->getSellPrice($mysoc, $projectstatic->thirdparty, 0);
|
||||
|
||||
$pu_htline = empty($dataforprice['pu_ht']) ? 0 : $dataforprice['pu_ht'];
|
||||
$txtvaline = $dataforprice['tva_tx'];
|
||||
$localtax1line = $dataforprice['localtax1'];
|
||||
$localtax2line = $dataforprice['localtax2'];
|
||||
|
||||
$product_data_cache[$fk_product] = array('duration'=>$prodDurationHours,'dataforprice'=>$dataforprice);
|
||||
} else {
|
||||
$prodDurationHours = $product_data_cache[$fk_product]['duration'];
|
||||
$pu_htline = empty($product_data_cache[$fk_product]['dataforprice']['pu_ht']) ? 0 : $product_data_cache[$fk_product]['dataforprice']['pu_ht'];
|
||||
$txtvaline = $product_data_cache[$fk_product]['dataforprice']['tva_tx'];
|
||||
$localtax1line = $product_data_cache[$fk_product]['dataforprice']['localtax1'];
|
||||
$localtax2line = $product_data_cache[$fk_product]['dataforprice']['localtax2'];
|
||||
}
|
||||
$idprodline=$fk_product;
|
||||
}
|
||||
$pa_ht = price2num($value['totalvaluetodivideby3600'] / $value['timespent'], 'MU') * $prodDurationHours;
|
||||
} else {
|
||||
// If not product used, we use the hour unit for duration and unit price.
|
||||
$pu_ht_for_task = 0;
|
||||
// If we want to reuse the value of timespent (so use same price than cost price)
|
||||
if (!empty($conf->global->PROJECT_TIME_SPENT_INTO_INVOICE_USE_VALUE)) {
|
||||
$pu_ht_for_task = price2num($value['totalvaluetodivideby3600'] / $value['timespent'], 'MU');
|
||||
|
||||
|
||||
if ($idprodline > 0) {
|
||||
// If a product is defined, we msut use the $prodDurationHours and $pu_ht of product (already set previously).
|
||||
$pu_ht_for_task = $pu_htline;
|
||||
// If we want to reuse the value of timespent (so use same price than cost price)
|
||||
if (!empty($conf->global->PROJECT_TIME_SPENT_INTO_INVOICE_USE_VALUE)) {
|
||||
$pu_ht_for_task = price2num($timespent_data['totalvaluetodivideby3600'] / $timespent_data['timespent'], 'MU') * $prodDurationHours;
|
||||
}
|
||||
$pa_ht = price2num($timespent_data['totalvaluetodivideby3600'] / $timespent_data['timespent'], 'MU') * $prodDurationHours;
|
||||
} else {
|
||||
// If not product used, we use the hour unit for duration and unit price.
|
||||
$pu_ht_for_task = 0;
|
||||
// If we want to reuse the value of timespent (so use same price than cost price)
|
||||
if (!empty($conf->global->PROJECT_TIME_SPENT_INTO_INVOICE_USE_VALUE)) {
|
||||
$pu_ht_for_task = price2num($timespent_data['totalvaluetodivideby3600'] / $timespent_data['timespent'], 'MU');
|
||||
}
|
||||
$pa_ht = price2num($timespent_data['totalvaluetodivideby3600'] / $timespent_data['timespent'], 'MU');
|
||||
}
|
||||
$pa_ht = price2num($value['totalvaluetodivideby3600'] / $value['timespent'], 'MU');
|
||||
}
|
||||
|
||||
// Add lines
|
||||
$date_start = '';
|
||||
$date_end = '';
|
||||
$lineName = $ftask->ref.' - '.$ftask->label;
|
||||
$lineid = $tmpinvoice->addline($lineName, $pu_ht_for_task, price2num($qtyhour / $prodDurationHours, 'MS'), $txtva, $localtax1, $localtax2, ($idprod > 0 ? $idprod : 0), 0, $date_start, $date_end, 0, 0, '', 'HT', 0, 1, -1, 0, '', 0, 0, null, $pa_ht);
|
||||
if ($lineid < 0) {
|
||||
$error++;
|
||||
setEventMessages($tmpinvoice->error, $tmpinvoice->errors, 'errors');
|
||||
break;
|
||||
}
|
||||
|
||||
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 .= ' WHERE rowid IN ('.$db->sanitize(join(',', $toselect)).')';
|
||||
$result = $db->query($sql);
|
||||
if (!$result) {
|
||||
// Add lines
|
||||
$date_start = '';
|
||||
$date_end = '';
|
||||
$lineName = $ftask->ref . ' - ' . $ftask->label;
|
||||
$lineid = $tmpinvoice->addline($lineName, $pu_ht_for_task, price2num($qtyhour / $prodDurationHours, 'MS'), $txtvaline, $localtax1line, $localtax2line, ($idprodline > 0 ? $idprodline : 0), 0, $date_start, $date_end, 0, 0, '', 'HT', 0, 1, -1, 0, '', 0, 0, null, $pa_ht);
|
||||
if ($lineid < 0) {
|
||||
$error++;
|
||||
setEventMessages($db->lasterror(), null, 'errors');
|
||||
setEventMessages($tmpinvoice->error, $tmpinvoice->errors, 'errors');
|
||||
break;
|
||||
}
|
||||
|
||||
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 .= ' WHERE rowid IN (' . $db->sanitize(join(',', $toselect)) . ')';
|
||||
$result = $db->query($sql);
|
||||
if (!$result) {
|
||||
$error++;
|
||||
setEventMessages($db->lasterror(), null, 'errors');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1085,6 +1207,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));
|
||||
@ -1312,6 +1437,7 @@ 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,";
|
||||
@ -1324,9 +1450,10 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser
|
||||
$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."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."user as u ON t.fk_user = u.rowid";
|
||||
$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";
|
||||
|
||||
// Add table from hooks
|
||||
@ -1363,6 +1490,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';
|
||||
}
|
||||
@ -1461,6 +1591,10 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser
|
||||
print '<td>'.$langs->trans("ProgressDeclared").'</td>';
|
||||
if (empty($conf->global->PROJECT_HIDE_TASKS) && !empty($conf->global->PROJECT_BILL_TIME_SPENT)) {
|
||||
print '<td></td>';
|
||||
|
||||
if ($conf->service->enabled && $projectstatic->thirdparty->id > 0 && $projectstatic->usage_bill_time) {
|
||||
print '<td>'.$langs->trans("Product").'</td>';
|
||||
}
|
||||
}
|
||||
// Hook fields
|
||||
$parameters = array('mode' => 'create');
|
||||
@ -1536,6 +1670,12 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser
|
||||
if (empty($conf->global->PROJECT_HIDE_TASKS) && !empty($conf->global->PROJECT_BILL_TIME_SPENT)) {
|
||||
print '<td>';
|
||||
print '</td>';
|
||||
|
||||
if ($conf->service->enabled && $projectstatic->thirdparty->id > 0 && $projectstatic->usage_bill_time) {
|
||||
print '<td class="nowrap">';
|
||||
print $form->select_produits('', 'fk_product', '1', 0, $projectstatic->thirdparty->price_level, 1, 2, '', 0, array(), $projectstatic->thirdparty->id, 'None', 0, 'maxwidth500');
|
||||
print '</td>';
|
||||
}
|
||||
}
|
||||
|
||||
// Fields from hook
|
||||
@ -1619,6 +1759,10 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser
|
||||
if (!empty($arrayfields['t.task_duration']['checked'])) {
|
||||
print '<td class="liste_titre right"></td>';
|
||||
}
|
||||
// Product
|
||||
if (!empty($arrayfields['t.fk_product']['checked'])) {
|
||||
print '<td class="liste_titre right"></td>';
|
||||
}
|
||||
// Value in main currency
|
||||
if (!empty($arrayfields['value']['checked'])) {
|
||||
print '<td class="liste_titre"></td>';
|
||||
@ -1670,6 +1814,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);
|
||||
}
|
||||
|
||||
if (!empty($arrayfields['value']['checked'])) {
|
||||
print_liste_field_titre($arrayfields['value']['label'], $_SERVER['PHP_SELF'], '', '', $param, '', $sortfield, $sortorder, 'right ');
|
||||
}
|
||||
@ -1860,6 +2008,23 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser
|
||||
$totalarray['totalduration'] += $task_time->task_duration;
|
||||
}
|
||||
|
||||
//Product
|
||||
if (!empty($arrayfields['t.fk_product']['checked'])) {
|
||||
print '<td class="nowraponall">';
|
||||
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 '</td>';
|
||||
}
|
||||
|
||||
// Value spent
|
||||
if (!empty($arrayfields['value']['checked'])) {
|
||||
$langs->load("salaries");
|
||||
|
||||
Loading…
Reference in New Issue
Block a user