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:
Laurent Destailleur 2020-01-15 13:23:25 +01:00 committed by GitHub
commit 43bf0a79e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 1168 additions and 880 deletions

View File

@ -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.='&nbsp;';
$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;
}
}
} }

View File

@ -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

View File

@ -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');