Merge pull request #12770 from guillaumematheron/develop
NEW 2 new options when creating an invoice from time spent on a project : by period or by task
This commit is contained in:
commit
43bf0a79e6
@ -7842,4 +7842,171 @@ class Form
|
|||||||
|
|
||||||
return $out;
|
return $out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Output a combo list with invoices qualified for a third party
|
||||||
|
*
|
||||||
|
* @param int $socid Id third party (-1=all, 0=only projects not linked to a third party, id=projects not linked or linked to third party id)
|
||||||
|
* @param int $selected Id invoice preselected
|
||||||
|
* @param string $htmlname Name of HTML select
|
||||||
|
* @param int $maxlength Maximum length of label
|
||||||
|
* @param int $option_only Return only html options lines without the select tag
|
||||||
|
* @param string $show_empty Add an empty line ('1' or string to show for empty line)
|
||||||
|
* @param int $discard_closed Discard closed projects (0=Keep,1=hide completely,2=Disable)
|
||||||
|
* @param int $forcefocus Force focus on field (works with javascript only)
|
||||||
|
* @param int $disabled Disabled
|
||||||
|
* @param string $morecss More css added to the select component
|
||||||
|
* @param string $projectsListId ''=Automatic filter on project allowed. List of id=Filter on project ids.
|
||||||
|
* @param string $showproject 'all' = Show project info, ''=Hide project info
|
||||||
|
* @param User $usertofilter User object to use for filtering
|
||||||
|
* @return int Nbr of project if OK, <0 if KO
|
||||||
|
*/
|
||||||
|
public function selectInvoice($socid = -1, $selected = '', $htmlname = 'invoiceid', $maxlength = 24, $option_only = 0, $show_empty = '1', $discard_closed = 0, $forcefocus = 0, $disabled = 0, $morecss = 'maxwidth500', $projectsListId = '', $showproject = 'all', $usertofilter = null)
|
||||||
|
{
|
||||||
|
global $user,$conf,$langs;
|
||||||
|
|
||||||
|
require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
|
||||||
|
|
||||||
|
if (is_null($usertofilter))
|
||||||
|
{
|
||||||
|
$usertofilter = $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
$out='';
|
||||||
|
|
||||||
|
$hideunselectables = false;
|
||||||
|
if (! empty($conf->global->PROJECT_HIDE_UNSELECTABLES)) $hideunselectables = true;
|
||||||
|
|
||||||
|
if (empty($projectsListId))
|
||||||
|
{
|
||||||
|
if (empty($usertofilter->rights->projet->all->lire))
|
||||||
|
{
|
||||||
|
$projectstatic=new Project($this->db);
|
||||||
|
$projectsListId = $projectstatic->getProjectsAuthorizedForUser($usertofilter, 0, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search all projects
|
||||||
|
$sql = 'SELECT f.rowid, f.ref as fref, "nolabel" as flabel, p.rowid as pid, f.ref,
|
||||||
|
p.title, p.fk_soc, p.fk_statut, p.public,';
|
||||||
|
$sql.= ' s.nom as name';
|
||||||
|
$sql.= ' FROM '.MAIN_DB_PREFIX .'projet as p';
|
||||||
|
$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'societe as s ON s.rowid = p.fk_soc,';
|
||||||
|
$sql.= ' '.MAIN_DB_PREFIX.'facture as f';
|
||||||
|
$sql.= " WHERE p.entity IN (".getEntity('project').")";
|
||||||
|
$sql.= " AND f.fk_projet = p.rowid AND f.fk_statut=0"; //Brouillons seulement
|
||||||
|
//if ($projectsListId) $sql.= " AND p.rowid IN (".$projectsListId.")";
|
||||||
|
//if ($socid == 0) $sql.= " AND (p.fk_soc=0 OR p.fk_soc IS NULL)";
|
||||||
|
//if ($socid > 0) $sql.= " AND (p.fk_soc=".$socid." OR p.fk_soc IS NULL)";
|
||||||
|
$sql.= " GROUP BY f.ref ORDER BY p.ref, f.ref ASC";
|
||||||
|
|
||||||
|
$resql=$this->db->query($sql);
|
||||||
|
if ($resql)
|
||||||
|
{
|
||||||
|
// Use select2 selector
|
||||||
|
if (! empty($conf->use_javascript_ajax))
|
||||||
|
{
|
||||||
|
include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
|
||||||
|
$comboenhancement = ajax_combobox($htmlname, '', 0, $forcefocus);
|
||||||
|
$out.=$comboenhancement;
|
||||||
|
$morecss='minwidth200imp maxwidth500';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($option_only)) {
|
||||||
|
$out.= '<select class="valignmiddle flat'.($morecss?' '.$morecss:'').'"'.($disabled?' disabled="disabled"':'').' id="'.$htmlname.'" name="'.$htmlname.'">';
|
||||||
|
}
|
||||||
|
if (! empty($show_empty)) {
|
||||||
|
$out.= '<option value="0" class="optiongrey">';
|
||||||
|
if (! is_numeric($show_empty)) $out.=$show_empty;
|
||||||
|
else $out.=' ';
|
||||||
|
$out.= '</option>';
|
||||||
|
}
|
||||||
|
$num = $this->db->num_rows($resql);
|
||||||
|
$i = 0;
|
||||||
|
if ($num)
|
||||||
|
{
|
||||||
|
while ($i < $num)
|
||||||
|
{
|
||||||
|
$obj = $this->db->fetch_object($resql);
|
||||||
|
// If we ask to filter on a company and user has no permission to see all companies and project is linked to another company, we hide project.
|
||||||
|
if ($socid > 0 && (empty($obj->fk_soc) || $obj->fk_soc == $socid) && empty($usertofilter->rights->societe->lire))
|
||||||
|
{
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ($discard_closed == 1 && $obj->fk_statut == Project::STATUS_CLOSED)
|
||||||
|
{
|
||||||
|
$i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$labeltoshow = '';
|
||||||
|
|
||||||
|
if ($showproject == 'all')
|
||||||
|
{
|
||||||
|
$labeltoshow.=dol_trunc($obj->ref, 18); // Invoice ref
|
||||||
|
if ($obj->name) $labeltoshow.=' - '.$obj->name; // Soc name
|
||||||
|
|
||||||
|
$disabled=0;
|
||||||
|
if ($obj->fk_statut == Project::STATUS_DRAFT)
|
||||||
|
{
|
||||||
|
$disabled=1;
|
||||||
|
$labeltoshow.=' - '.$langs->trans("Draft");
|
||||||
|
}
|
||||||
|
elseif ($obj->fk_statut == Project::STATUS_CLOSED)
|
||||||
|
{
|
||||||
|
if ($discard_closed == 2) $disabled=1;
|
||||||
|
$labeltoshow.=' - '.$langs->trans("Closed");
|
||||||
|
}
|
||||||
|
elseif ($socid > 0 && (! empty($obj->fk_soc) && $obj->fk_soc != $socid))
|
||||||
|
{
|
||||||
|
$disabled=1;
|
||||||
|
$labeltoshow.=' - '.$langs->trans("LinkedToAnotherCompany");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($selected) && $selected == $obj->rowid)
|
||||||
|
{
|
||||||
|
$out.= '<option value="'.$obj->rowid.'" selected';
|
||||||
|
//if ($disabled) $out.=' disabled'; // with select2, field can't be preselected if disabled
|
||||||
|
$out.= '>'.$labeltoshow.'</option>';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ($hideunselectables && $disabled && ($selected != $obj->rowid))
|
||||||
|
{
|
||||||
|
$resultat='';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$resultat='<option value="'.$obj->rowid.'"';
|
||||||
|
if ($disabled) $resultat.=' disabled';
|
||||||
|
//if ($obj->public) $labeltoshow.=' ('.$langs->trans("Public").')';
|
||||||
|
//else $labeltoshow.=' ('.$langs->trans("Private").')';
|
||||||
|
$resultat.='>';
|
||||||
|
$resultat.=$labeltoshow;
|
||||||
|
$resultat.='</option>';
|
||||||
|
}
|
||||||
|
$out.= $resultat;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (empty($option_only)) {
|
||||||
|
$out.= '</select>';
|
||||||
|
}
|
||||||
|
|
||||||
|
print $out;
|
||||||
|
|
||||||
|
$this->db->free($resql);
|
||||||
|
return $num;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dol_print_error($this->db);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,6 +21,8 @@
|
|||||||
* \brief Fichier de la classe des fonctions predefinie de composants html autre
|
* \brief Fichier de la classe des fonctions predefinie de composants html autre
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Classe permettant la generation de composants html autre
|
* Classe permettant la generation de composants html autre
|
||||||
|
|||||||
@ -342,9 +342,26 @@ if ($action == 'confirm_generateinvoice')
|
|||||||
|
|
||||||
$db->begin();
|
$db->begin();
|
||||||
$idprod = GETPOST('productid', 'int');
|
$idprod = GETPOST('productid', 'int');
|
||||||
|
$generateinvoicemode = GETPOST('generateinvoicemode', 'string');
|
||||||
|
$invoiceToUse = GETPOST('invoiceid', 'int');
|
||||||
|
|
||||||
|
$prodDurationHours = 1.0;
|
||||||
if ($idprod > 0)
|
if ($idprod > 0)
|
||||||
{
|
{
|
||||||
$tmpproduct->fetch($idprod);
|
$tmpproduct->fetch($idprod);
|
||||||
|
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);
|
$dataforprice = $tmpproduct->getSellPrice($mysoc, $projectstatic->thirdparty, 0);
|
||||||
$pu_ht = empty($dataforprice['pu_ht']) ? 0 : $dataforprice['pu_ht'];
|
$pu_ht = empty($dataforprice['pu_ht']) ? 0 : $dataforprice['pu_ht'];
|
||||||
@ -364,15 +381,21 @@ if ($action == 'confirm_generateinvoice')
|
|||||||
$tmpinvoice->date = dol_mktime(GETPOST('rehour', 'int'), GETPOST('remin', 'int'), GETPOST('resec', 'int'), GETPOST('remonth', 'int'), GETPOST('reday', 'int'), GETPOST('reyear', 'int'));
|
$tmpinvoice->date = dol_mktime(GETPOST('rehour', 'int'), GETPOST('remin', 'int'), GETPOST('resec', 'int'), GETPOST('remonth', 'int'), GETPOST('reday', 'int'), GETPOST('reyear', 'int'));
|
||||||
$tmpinvoice->fk_project = $projectstatic->id;
|
$tmpinvoice->fk_project = $projectstatic->id;
|
||||||
|
|
||||||
|
if ($invoiceToUse) {
|
||||||
|
$tmpinvoice->fetch($invoiceToUse);
|
||||||
|
}
|
||||||
|
else {
|
||||||
$result = $tmpinvoice->create($user);
|
$result = $tmpinvoice->create($user);
|
||||||
if ($result <= 0)
|
if ($result <= 0)
|
||||||
{
|
{
|
||||||
$error++;
|
$error++;
|
||||||
setEventMessages($tmpinvoice->error, $tmpinvoice->errors, 'errors');
|
setEventMessages($tmpinvoice->error, $tmpinvoice->errors, 'errors');
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!$error)
|
if (!$error)
|
||||||
{
|
{
|
||||||
|
if ($generateinvoicemode=='onelineperuser') {
|
||||||
$arrayoftasks = array();
|
$arrayoftasks = array();
|
||||||
foreach ($toselect as $key => $value)
|
foreach ($toselect as $key => $value)
|
||||||
{
|
{
|
||||||
@ -389,7 +412,7 @@ if ($action == 'confirm_generateinvoice')
|
|||||||
$username = $fuser->getFullName($langs);
|
$username = $fuser->getFullName($langs);
|
||||||
|
|
||||||
// Define qty per hour
|
// Define qty per hour
|
||||||
$qtyhour = round($value['timespent'] / 3600, 2);
|
$qtyhour = $value['timespent'] / 3600;
|
||||||
$qtyhourtext = convertSecondToTime($value['timespent'], 'all', $conf->global->MAIN_DURATION_OF_WORKDAY);
|
$qtyhourtext = convertSecondToTime($value['timespent'], 'all', $conf->global->MAIN_DURATION_OF_WORKDAY);
|
||||||
|
|
||||||
// If no unit price known
|
// If no unit price known
|
||||||
@ -399,7 +422,7 @@ if ($action == 'confirm_generateinvoice')
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add lines
|
// Add lines
|
||||||
$lineid = $tmpinvoice->addline($langs->trans("TimeSpentForInvoice", $username).' : '.$qtyhourtext, $pu_ht, $qtyhour, $txtva, $localtax1, $localtax2, ($idprod > 0 ? $idprod : 0));
|
$lineid = $tmpinvoice->addline($langs->trans("TimeSpentForInvoice", $username).' : '.$qtyhourtext, $pu_ht, round($qtyhour/$prodDurationHours, 2), $txtva, $localtax1, $localtax2, ($idprod > 0 ? $idprod : 0));
|
||||||
|
|
||||||
// Update lineid into line of timespent
|
// Update lineid into line of timespent
|
||||||
$sql = 'UPDATE '.MAIN_DB_PREFIX.'projet_task_time SET invoice_line_id = '.$lineid.', invoice_id = '.$tmpinvoice->id;
|
$sql = 'UPDATE '.MAIN_DB_PREFIX.'projet_task_time SET invoice_line_id = '.$lineid.', invoice_id = '.$tmpinvoice->id;
|
||||||
@ -413,6 +436,92 @@ if ($action == 'confirm_generateinvoice')
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
elseif ($generateinvoicemode=='onelineperperiod') {
|
||||||
|
$arrayoftasks = array();
|
||||||
|
foreach ($toselect as $key => $value)
|
||||||
|
{
|
||||||
|
// Get userid, timepent
|
||||||
|
$object->fetchTimeSpent($value);
|
||||||
|
$arrayoftasks[$object->timespent_id]['timespent'] = $object->timespent_duration;
|
||||||
|
$arrayoftasks[$object->timespent_id]['totalvaluetodivideby3600'] = $object->timespent_duration * $object->timespent_thm;
|
||||||
|
$arrayoftasks[$object->timespent_id]['note'] = $object->timespent_note;
|
||||||
|
$arrayoftasks[$object->timespent_id]['user'] = $object->timespent_fk_user;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($arrayoftasks as $timespent_id => $value)
|
||||||
|
{
|
||||||
|
$userid = $value['user'];
|
||||||
|
$fuser->fetch($userid);
|
||||||
|
//$pu_ht = $value['timespent'] * $fuser->thm;
|
||||||
|
$username = $fuser->getFullName($langs);
|
||||||
|
|
||||||
|
// 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($value['totalvaluetodivideby3600'] / 3600, 'MU');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add lines
|
||||||
|
$lineid = $tmpinvoice->addline($value['note'], $pu_ht, round($qtyhour/$prodDurationHours, 2), $txtva, $localtax1, $localtax2, ($idprod > 0 ? $idprod : 0));
|
||||||
|
|
||||||
|
// Update lineid into line of timespent
|
||||||
|
$sql = 'UPDATE '.MAIN_DB_PREFIX.'projet_task_time SET invoice_line_id = '.$lineid.', invoice_id = '.$tmpinvoice->id;
|
||||||
|
$sql .= ' WHERE rowid in ('.join(',', $toselect).') AND fk_user = '.$userid;
|
||||||
|
$result = $db->query($sql);
|
||||||
|
if (!$result)
|
||||||
|
{
|
||||||
|
$error++;
|
||||||
|
setEventMessages($db->lasterror(), null, 'errors');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif ($generateinvoicemode=='onelinepertask') {
|
||||||
|
$arrayoftasks = array();
|
||||||
|
foreach ($toselect as $key => $value)
|
||||||
|
{
|
||||||
|
// Get userid, timepent
|
||||||
|
$object->fetchTimeSpent($value);
|
||||||
|
// $object->id is the task id
|
||||||
|
$arrayoftasks[$object->id]['timespent'] += $object->timespent_duration;
|
||||||
|
$arrayoftasks[$object->id]['totalvaluetodivideby3600'] += $object->timespent_duration * $object->timespent_thm;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($arrayoftasks as $task_id => $value)
|
||||||
|
{
|
||||||
|
$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 no unit price known
|
||||||
|
if (empty($pu_ht))
|
||||||
|
{
|
||||||
|
$pu_ht = price2num($value['totalvaluetodivideby3600'] / 3600, 'MU');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add lines
|
||||||
|
$lineName = $ftask->ref.' - '.$ftask->label;
|
||||||
|
$lineid = $tmpinvoice->addline($lineName, $pu_ht, round($qtyhour/$prodDurationHours, 2), $txtva, $localtax1, $localtax2, ($idprod > 0 ? $idprod : 0));
|
||||||
|
|
||||||
|
// Update lineid into line of timespent
|
||||||
|
$sql = 'UPDATE '.MAIN_DB_PREFIX.'projet_task_time SET invoice_line_id = '.$lineid.', invoice_id = '.$tmpinvoice->id;
|
||||||
|
$sql .= ' WHERE rowid in ('.join(',', $toselect).')';
|
||||||
|
$result = $db->query($sql);
|
||||||
|
if (!$result)
|
||||||
|
{
|
||||||
|
$error++;
|
||||||
|
setEventMessages($db->lasterror(), null, 'errors');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!$error)
|
if (!$error)
|
||||||
{
|
{
|
||||||
@ -841,7 +950,8 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0)
|
|||||||
print '<td>';
|
print '<td>';
|
||||||
$tmparray = array(
|
$tmparray = array(
|
||||||
'onelineperuser'=>'OneLinePerUser',
|
'onelineperuser'=>'OneLinePerUser',
|
||||||
//'onelinepertask'=>'OneLinePerTask',
|
'onelinepertask'=>'OneLinePerTask',
|
||||||
|
'onelineperperiod'=>'OneLinePerPeriod',
|
||||||
);
|
);
|
||||||
print $form->selectarray('generateinvoicemode', $tmparray, 'onelineperuser', 0, 0, 0, '', 1);
|
print $form->selectarray('generateinvoicemode', $tmparray, 'onelineperuser', 0, 0, 0, '', 1);
|
||||||
print '</td>';
|
print '</td>';
|
||||||
@ -857,6 +967,15 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0)
|
|||||||
print '</td>';
|
print '</td>';
|
||||||
print '</tr>';
|
print '</tr>';
|
||||||
}
|
}
|
||||||
|
print '<tr>';
|
||||||
|
print '<td class="titlefield">';
|
||||||
|
print $langs->trans('InvoiceToUse');
|
||||||
|
print '</td>';
|
||||||
|
print '<td>';
|
||||||
|
$form->selectInvoice('invoice', '', 'invoiceid', 24, 0, $langs->trans('NewInvoice'),
|
||||||
|
1, 0, 0, 'maxwidth500', '', 'all');
|
||||||
|
print '</td>';
|
||||||
|
print '</tr>';
|
||||||
/*print '<tr>';
|
/*print '<tr>';
|
||||||
print '<td>';
|
print '<td>';
|
||||||
print $langs->trans('ValidateInvoices');
|
print $langs->trans('ValidateInvoices');
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user