From d66f54d06d9622b2b41da7d046c1eaa7671233d8 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 20 May 2020 22:45:43 +0200 Subject: [PATCH 01/10] Clean home page --- htdocs/index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/index.php b/htdocs/index.php index 7f6e462839b..2301a841ddc 100644 --- a/htdocs/index.php +++ b/htdocs/index.php @@ -80,7 +80,7 @@ llxHeader('', $title); $resultboxes = FormOther::getBoxesArea($user, "0"); // Load $resultboxes (selectboxlist + boxactivated + boxlista + boxlistb) -print load_fiche_titre($langs->trans("HomeArea"), $resultboxes['selectboxlist'], 'home', 0, '', 'titleforhome'); +print load_fiche_titre(' ', $resultboxes['selectboxlist'], '', 0, '', 'titleforhome'); if (!empty($conf->global->MAIN_MOTD)) { From c4d1db263c4e1dc961e0b1f21da104863175be91 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 20 May 2020 23:15:18 +0200 Subject: [PATCH 02/10] Fix end of workflow of MO --- htdocs/langs/en_US/agenda.lang | 2 ++ htdocs/mrp/class/mo.class.php | 6 +++--- htdocs/mrp/mo_card.php | 34 +++++++++++++++++++++++++++++++++- htdocs/mrp/mo_production.php | 32 ++++++++++++++++++++++++++++++++ 4 files changed, 70 insertions(+), 4 deletions(-) diff --git a/htdocs/langs/en_US/agenda.lang b/htdocs/langs/en_US/agenda.lang index f4c8c5b4fbc..6463b4eb9a0 100644 --- a/htdocs/langs/en_US/agenda.lang +++ b/htdocs/langs/en_US/agenda.lang @@ -112,8 +112,10 @@ BOM_CLOSEInDolibarr=BOM disabled BOM_REOPENInDolibarr=BOM reopen BOM_DELETEInDolibarr=BOM deleted MRP_MO_VALIDATEInDolibarr=MO validated +MRP_MO_UNVALIDATEInDolibarr=MO set to draft status MRP_MO_PRODUCEDInDolibarr=MO produced MRP_MO_DELETEInDolibarr=MO deleted +MRP_MO_CANCELInDolibarr=MO canceled ##### End agenda events ##### AgendaModelModule=Document templates for event DateActionStart=Start date diff --git a/htdocs/mrp/class/mo.class.php b/htdocs/mrp/class/mo.class.php index 07c6eae52a1..e16f7fec71a 100644 --- a/htdocs/mrp/class/mo.class.php +++ b/htdocs/mrp/class/mo.class.php @@ -929,7 +929,7 @@ class Mo extends CommonObject return -1; }*/ - return $this->setStatusCommon($user, self::STATUS_DRAFT, $notrigger, 'MO_UNVALIDATE'); + return $this->setStatusCommon($user, self::STATUS_DRAFT, $notrigger, 'MRP_MO_UNVALIDATE'); } /** @@ -954,7 +954,7 @@ class Mo extends CommonObject return -1; }*/ - return $this->setStatusCommon($user, self::STATUS_CANCELED, $notrigger, 'MO_CLOSE'); + return $this->setStatusCommon($user, self::STATUS_CANCELED, $notrigger, 'MRP_MO_CANCEL'); } /** @@ -967,7 +967,7 @@ class Mo extends CommonObject public function reopen($user, $notrigger = 0) { // Protection - if ($this->status != self::STATUS_CANCELED) + if ($this->status != self::STATUS_PRODUCED && $this->status != self::STATUS_CANCELED) { return 0; } diff --git a/htdocs/mrp/mo_card.php b/htdocs/mrp/mo_card.php index 86b2b886dea..0e2cc09e811 100644 --- a/htdocs/mrp/mo_card.php +++ b/htdocs/mrp/mo_card.php @@ -169,6 +169,35 @@ if (empty($reshook)) { $object->setProject(GETPOST('projectid', 'int')); } + + // Action close produced + if ($action == 'confirm_produced' && $confirm == 'yes' && $permissiontoadd) + { + $result = $object->setStatut($object::STATUS_PRODUCED, 0, '', 'MRP_MO_PRODUCED'); + if ($result >= 0) + { + // Define output language + if (empty($conf->global->MAIN_DISABLE_PDF_AUTOUPDATE)) + { + $outputlangs = $langs; + $newlang = ''; + if ($conf->global->MAIN_MULTILANGS && empty($newlang) && GETPOST('lang_id', 'aZ09')) $newlang = GETPOST('lang_id', 'aZ09'); + if ($conf->global->MAIN_MULTILANGS && empty($newlang)) $newlang = $object->thirdparty->default_lang; + if (!empty($newlang)) { + $outputlangs = new Translate("", $conf); + $outputlangs->setDefaultLang($newlang); + } + $model = $object->modelpdf; + $ret = $object->fetch($id); // Reload to get new records + + $object->generateDocument($model, $outputlangs, 0, 0, 0); + } + } + else + { + setEventMessages($object->error, $object->errors, 'errors'); + } + } } @@ -630,10 +659,13 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea { if ($object->status == $object::STATUS_VALIDATED || $object->status == $object::STATUS_INPROGRESS) { + // TODO If production is already > 1, show only close, else show cancel + print ''.$langs->trans("Close").''."\n"; + print ''.$langs->trans("Cancel").''."\n"; } - if ($object->status == $object::STATUS_CANCELED) + if ($object->status == $object::STATUS_PRODUCED || $object->status == $object::STATUS_CANCELED) { print ''.$langs->trans("Re-Open").''."\n"; } diff --git a/htdocs/mrp/mo_production.php b/htdocs/mrp/mo_production.php index f762ca9291c..9f05283345a 100644 --- a/htdocs/mrp/mo_production.php +++ b/htdocs/mrp/mo_production.php @@ -383,6 +383,35 @@ if (empty($reshook)) exit; } } + + // Action close produced + if ($action == 'confirm_produced' && $confirm == 'yes' && $permissiontoadd) + { + $result = $object->setStatut($object::STATUS_PRODUCED, 0, '', 'MRP_MO_PRODUCED'); + if ($result >= 0) + { + // Define output language + if (empty($conf->global->MAIN_DISABLE_PDF_AUTOUPDATE)) + { + $outputlangs = $langs; + $newlang = ''; + if ($conf->global->MAIN_MULTILANGS && empty($newlang) && GETPOST('lang_id', 'aZ09')) $newlang = GETPOST('lang_id', 'aZ09'); + if ($conf->global->MAIN_MULTILANGS && empty($newlang)) $newlang = $object->thirdparty->default_lang; + if (!empty($newlang)) { + $outputlangs = new Translate("", $conf); + $outputlangs->setDefaultLang($newlang); + } + $model = $object->modelpdf; + $ret = $object->fetch($id); // Reload to get new records + + $object->generateDocument($model, $outputlangs, 0, 0, 0); + } + } + else + { + setEventMessages($object->error, $object->errors, 'errors'); + } + } } @@ -599,6 +628,9 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea { if ($object->status == $object::STATUS_VALIDATED || $object->status == $object::STATUS_INPROGRESS) { + // TODO If production is already > 1, show only close, else show cancel + print ''.$langs->trans("Close").''."\n"; + print ''.$langs->trans("Cancel").''."\n"; } From e42fb1b57d299d6ba8a7fb2742460fc71dc1ef36 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 20 May 2020 23:15:56 +0200 Subject: [PATCH 03/10] Fix trigger name --- htdocs/mrp/class/mo.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/mrp/class/mo.class.php b/htdocs/mrp/class/mo.class.php index e16f7fec71a..89e9b25abef 100644 --- a/htdocs/mrp/class/mo.class.php +++ b/htdocs/mrp/class/mo.class.php @@ -979,7 +979,7 @@ class Mo extends CommonObject return -1; }*/ - return $this->setStatusCommon($user, self::STATUS_VALIDATED, $notrigger, 'MO_REOPEN'); + return $this->setStatusCommon($user, self::STATUS_VALIDATED, $notrigger, 'MRP_MO_REOPEN'); } /** From 650bddde0d6942fcaa7fe2d383a2ebb7b2d485e1 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 20 May 2020 23:25:10 +0200 Subject: [PATCH 04/10] Fix phpcs --- htdocs/projet/class/project.class.php | 125 +++++++++++++------------- 1 file changed, 64 insertions(+), 61 deletions(-) diff --git a/htdocs/projet/class/project.class.php b/htdocs/projet/class/project.class.php index 78f5bac2fe7..1e6c675d6fe 100644 --- a/htdocs/projet/class/project.class.php +++ b/htdocs/projet/class/project.class.php @@ -1047,76 +1047,79 @@ class Project extends CommonObject if ($option != 'nolink') $label = ''.$langs->trans("ShowProject").''; $label .= ($label ? '
' : '').''.$langs->trans('Ref').': '.$this->ref; // The space must be after the : to not being explode when showing the title in img_picto $label .= ($label ? '
' : '').''.$langs->trans('Label').': '.$this->title; // The space must be after the : to not being explode when showing the title in img_picto - if (!empty($this->thirdparty_name)) + if (!empty($this->thirdparty_name)) { $label .= ($label ? '
' : '').''.$langs->trans('ThirdParty').': '.$this->thirdparty_name; // The space must be after the : to not being explode when showing the title in img_picto - if (!empty($this->dateo)) - $label .= ($label ? '
' : '').''.$langs->trans('DateStart').': '.dol_print_date($this->dateo, 'day'); // The space must be after the : to not being explode when showing the title in img_picto - if (!empty($this->datee)) - $label .= ($label ? '
' : '').''.$langs->trans('DateEnd').': '.dol_print_date($this->datee, 'day'); // The space must be after the : to not being explode when showing the title in img_picto - if ($moreinpopup) $label .= '
'.$moreinpopup; + } + if (!empty($this->dateo)) { + $label .= ($label ? '
' : '').''.$langs->trans('DateStart').': '.dol_print_date($this->dateo, 'day'); // The space must be after the : to not being explode when showing the title in img_picto + } + if (!empty($this->datee)) { + $label .= ($label ? '
' : '').''.$langs->trans('DateEnd').': '.dol_print_date($this->datee, 'day'); // The space must be after the : to not being explode when showing the title in img_picto + } + if ($moreinpopup) $label .= '
'.$moreinpopup; - $url = ''; - if ($option != 'nolink') - { - if (preg_match('/\.php$/', $option)) { - $url = dol_buildpath($option, 1).'?id='.$this->id; - } - elseif ($option == 'task') - { - $url = DOL_URL_ROOT.'/projet/tasks.php?id='.$this->id; - } - else - { - $url = DOL_URL_ROOT.'/projet/card.php?id='.$this->id; - } - // 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 ($add_save_lastsearch_values) $url .= '&save_lastsearch_values=1'; - } + $url = ''; + if ($option != 'nolink') + { + if (preg_match('/\.php$/', $option)) { + $url = dol_buildpath($option, 1).'?id='.$this->id; + } + elseif ($option == 'task') + { + $url = DOL_URL_ROOT.'/projet/tasks.php?id='.$this->id; + } + else + { + $url = DOL_URL_ROOT.'/projet/card.php?id='.$this->id; + } + // 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 ($add_save_lastsearch_values) $url .= '&save_lastsearch_values=1'; + } - $linkclose = ''; - if (empty($notooltip) && $user->rights->projet->lire) - { - if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) - { - $label = $langs->trans("ShowProject"); - $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"'; - } - $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"'; - $linkclose .= ' class="classfortooltip"'; + $linkclose = ''; + if (empty($notooltip) && $user->rights->projet->lire) + { + if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) + { + $label = $langs->trans("ShowProject"); + $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"'; + } + $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"'; + $linkclose .= ' class="classfortooltip"'; - /* - $hookmanager->initHooks(array('projectdao')); - $parameters=array('id'=>$this->id); - // Note that $action and $object may have been modified by some hooks - $reshook=$hookmanager->executeHooks('getnomurltooltip',$parameters,$this,$action); - if ($reshook > 0) - $linkclose = $hookmanager->resPrint; - */ - } + /* + $hookmanager->initHooks(array('projectdao')); + $parameters=array('id'=>$this->id); + // Note that $action and $object may have been modified by some hooks + $reshook=$hookmanager->executeHooks('getnomurltooltip',$parameters,$this,$action); + if ($reshook > 0) + $linkclose = $hookmanager->resPrint; + */ + } - $picto = 'projectpub'; - if (!$this->public) $picto = 'project'; + $picto = 'projectpub'; + if (!$this->public) $picto = 'project'; - $linkstart = ''; - $linkend = ''; + $linkstart = ''; + $linkend = ''; - $result .= $linkstart; - if ($withpicto) $result .= img_object(($notooltip ? '' : $label), $picto, ($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->title) ? $sep.dol_trunc($this->title, ($addlabel > 1 ? $addlabel : 0)) : ''); + $result .= $linkstart; + if ($withpicto) $result .= img_object(($notooltip ? '' : $label), $picto, ($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->title) ? $sep.dol_trunc($this->title, ($addlabel > 1 ? $addlabel : 0)) : ''); - global $action; - $hookmanager->initHooks(array('projectdao')); - $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; + global $action; + $hookmanager->initHooks(array('projectdao')); + $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 $result; } /** From de11fb2783198bbc4fd0ff2c0458f1132af336aa Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 20 May 2020 23:32:13 +0200 Subject: [PATCH 05/10] Fix phpcs --- htdocs/core/lib/project.lib.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/core/lib/project.lib.php b/htdocs/core/lib/project.lib.php index bf60968387a..bef3d9fd0dc 100644 --- a/htdocs/core/lib/project.lib.php +++ b/htdocs/core/lib/project.lib.php @@ -2226,7 +2226,7 @@ function print_projecttasks_array($db, $form, $socid, $projectsListId, $mytasks print ''; print ''; - print $projectstatic->getNomUrl(1, '', 0, '', '-', 0 , -1, 'nowraponall'); + print $projectstatic->getNomUrl(1, '', 0, '', '-', 0, -1, 'nowraponall'); if (!in_array('projectlabel', $hiddenfields)) print '
'.dol_trunc($objp->title, 24).''; print ''; print ''; From 92cbff7749c569fea6fb6f66e6c71d7f5beeeccc Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 20 May 2020 23:40:04 +0200 Subject: [PATCH 06/10] Clean code --- htdocs/comm/action/class/actioncomm.class.php | 21 ++++--------------- htdocs/core/modules/action/rapport.pdf.php | 1 - ...terface_50_modAgenda_ActionsAuto.class.php | 1 - .../install/mysql/tables/llx_actioncomm.sql | 1 - 4 files changed, 4 insertions(+), 20 deletions(-) diff --git a/htdocs/comm/action/class/actioncomm.class.php b/htdocs/comm/action/class/actioncomm.class.php index bf906989d16..3c060ffdc39 100644 --- a/htdocs/comm/action/class/actioncomm.class.php +++ b/htdocs/comm/action/class/actioncomm.class.php @@ -177,12 +177,6 @@ class ActionComm extends CommonObject */ public $fulldayevent = 0; - /** - * @var int Milestone - * @deprecated Milestone is already event with end date = start date - */ - public $punctual = 1; - /** * @var integer Percentage */ @@ -384,7 +378,6 @@ class ActionComm extends CommonObject if (empty($this->percentage)) $this->percentage = 0; if (empty($this->priority) || !is_numeric($this->priority)) $this->priority = 0; if (empty($this->fulldayevent)) $this->fulldayevent = 0; - if (empty($this->punctual)) $this->punctual = 0; if (empty($this->transparency)) $this->transparency = 0; if ($this->percentage > 100) $this->percentage = 100; //if ($this->percentage == 100 && ! $this->dateend) $this->dateend = $this->date; @@ -462,7 +455,7 @@ class ActionComm extends CommonObject $sql .= "fk_user_author,"; $sql .= "fk_user_action,"; $sql .= "fk_user_done,"; - $sql .= "label,percent,priority,fulldayevent,location,punctual,"; + $sql .= "label,percent,priority,fulldayevent,location,"; $sql .= "transparency,"; $sql .= "fk_element,"; $sql .= "elementtype,"; @@ -492,7 +485,7 @@ class ActionComm extends CommonObject $sql .= (isset($user->id) && $user->id > 0 ? $user->id : "null").", "; $sql .= ($userownerid > 0 ? $userownerid : "null").", "; $sql .= ($userdoneid > 0 ? $userdoneid : "null").", "; - $sql .= "'".$this->db->escape($this->label)."','".$this->db->escape($this->percentage)."','".$this->db->escape($this->priority)."','".$this->db->escape($this->fulldayevent)."','".$this->db->escape($this->location)."','".$this->db->escape($this->punctual)."', "; + $sql .= "'".$this->db->escape($this->label)."','".$this->db->escape($this->percentage)."','".$this->db->escape($this->priority)."','".$this->db->escape($this->fulldayevent)."','".$this->db->escape($this->location)."', "; $sql .= "'".$this->db->escape($this->transparency)."', "; $sql .= (!empty($this->fk_element) ? $this->fk_element : "null").", "; $sql .= (!empty($this->elementtype) ? "'".$this->db->escape($this->elementtype)."'" : "null").", "; @@ -693,7 +686,7 @@ class ActionComm extends CommonObject $sql .= " a.fk_user_action, a.fk_user_done,"; $sql .= " a.fk_contact, a.percent as percentage,"; $sql .= " a.fk_element as elementid, a.elementtype,"; - $sql .= " a.priority, a.fulldayevent, a.location, a.punctual, a.transparency,"; + $sql .= " a.priority, a.fulldayevent, a.location, a.transparency,"; $sql .= " c.id as type_id, c.code as type_code, c.libelle as type_label, c.color as type_color, c.picto as type_picto,"; $sql .= " s.nom as socname,"; $sql .= " u.firstname, u.lastname as lastname"; @@ -758,7 +751,6 @@ class ActionComm extends CommonObject $this->fulldayevent = $obj->fulldayevent; $this->location = $obj->location; $this->transparency = $obj->transparency; - $this->punctual = $obj->punctual; // deprecated $this->socid = $obj->fk_soc; // To have fetch_thirdparty method working $this->contactid = $obj->fk_contact; // To have fetch_contact method working @@ -1612,7 +1604,7 @@ class ActionComm extends CommonObject $sql .= " a.fk_user_action,"; $sql .= " a.fk_contact, a.percent as percentage,"; $sql .= " a.fk_element, a.elementtype,"; - $sql .= " a.priority, a.fulldayevent, a.location, a.punctual, a.transparency,"; + $sql .= " a.priority, a.fulldayevent, a.location, a.transparency,"; $sql .= " u.firstname, u.lastname, u.email,"; $sql .= " s.nom as socname,"; $sql .= " c.id as type_id, c.code as type_code, c.libelle as type_label"; @@ -1719,7 +1711,6 @@ class ActionComm extends CommonObject $event['fulldayevent'] = $obj->fulldayevent; $event['location'] = $obj->location; $event['transparency'] = (($obj->transparency > 0) ? 'OPAQUE' : 'TRANSPARENT'); // OPAQUE (busy) or TRANSPARENT (not busy) - $event['punctual'] = $obj->punctual; $event['category'] = $obj->type_label; $event['email'] = $obj->email; // Define $urlwithroot @@ -1929,11 +1920,7 @@ class ActionComm extends CommonObject $this->datem = $now; $this->datep = $now; $this->datef = $now; - $this->author = $user; - $this->usermod = $user; - $this->usertodo = $user; $this->fulldayevent = 0; - $this->punctual = 0; $this->percentage = 0; $this->location = 'Location'; $this->transparency = 1; // 1 means opaque diff --git a/htdocs/core/modules/action/rapport.pdf.php b/htdocs/core/modules/action/rapport.pdf.php index 1fb4e33a6c6..b978bed1eab 100644 --- a/htdocs/core/modules/action/rapport.pdf.php +++ b/htdocs/core/modules/action/rapport.pdf.php @@ -255,7 +255,6 @@ class CommActionRapport $eventstatic->id = $obj->id; $eventstatic->percentage = $obj->percent; $eventstatic->fulldayevent = $obj->fulldayevent; - $eventstatic->punctual = $obj->punctual; $y = max($y, $pdf->GetY(), $y0, $y1, $y2, $y3); diff --git a/htdocs/core/triggers/interface_50_modAgenda_ActionsAuto.class.php b/htdocs/core/triggers/interface_50_modAgenda_ActionsAuto.class.php index 3206e9377d3..49939277f49 100644 --- a/htdocs/core/triggers/interface_50_modAgenda_ActionsAuto.class.php +++ b/htdocs/core/triggers/interface_50_modAgenda_ActionsAuto.class.php @@ -898,7 +898,6 @@ class InterfaceActionsAuto extends DolibarrTriggers $actioncomm->datep = $now; $actioncomm->datef = $now; $actioncomm->durationp = 0; - $actioncomm->punctual = 1; $actioncomm->percentage = -1; // Not applicable $actioncomm->socid = $societeforaction->id; $actioncomm->contactid = $contactforaction->id; diff --git a/htdocs/install/mysql/tables/llx_actioncomm.sql b/htdocs/install/mysql/tables/llx_actioncomm.sql index 1cd3c9cf27e..8c50964eb59 100644 --- a/htdocs/install/mysql/tables/llx_actioncomm.sql +++ b/htdocs/install/mysql/tables/llx_actioncomm.sql @@ -48,7 +48,6 @@ create table llx_actioncomm priority smallint, -- priority (ical standard) visibility varchar(12) DEFAULT 'default', -- visibility (ical standard) - 'default', 'public', 'private', 'confidential' fulldayevent smallint NOT NULL default 0, -- full day (ical standard) - punctual smallint NOT NULL default 1, -- deprecated. milestone is event with date start (datep) = date end (datep2) percent smallint NOT NULL default 0, location varchar(128), durationp real, -- planed duration From 662970f2328e99dd0165f685442f5c74ee9f9e6e Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 20 May 2020 23:42:28 +0200 Subject: [PATCH 07/10] Clean sql file --- htdocs/install/mysql/migration/11.0.0-12.0.0.sql | 2 ++ 1 file changed, 2 insertions(+) diff --git a/htdocs/install/mysql/migration/11.0.0-12.0.0.sql b/htdocs/install/mysql/migration/11.0.0-12.0.0.sql index b20d3d9c782..e2112b020b0 100644 --- a/htdocs/install/mysql/migration/11.0.0-12.0.0.sql +++ b/htdocs/install/mysql/migration/11.0.0-12.0.0.sql @@ -74,6 +74,8 @@ delete from llx_const where name = 'PROJECT_HIDE_TASKS' and entity = 0; -- VMYSQL4.1 DROP INDEX ix_fk_product_stock on llx_product_batch; -- VPGSQL8.2 DROP INDEX ix_fk_product_stock +ALTER TABLE llx_actioncomm DROP COLUMN punctual; + DELETE FROM llx_menu where module='supplier_proposal'; UPDATE llx_website SET lang = 'en' WHERE lang like 'en_%'; From 036617570e1649df540a7c8c3f413567b4692fd8 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 20 May 2020 23:46:48 +0200 Subject: [PATCH 08/10] Doc --- htdocs/core/class/conf.class.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/htdocs/core/class/conf.class.php b/htdocs/core/class/conf.class.php index dee4edc3298..10d6fedf0e0 100644 --- a/htdocs/core/class/conf.class.php +++ b/htdocs/core/class/conf.class.php @@ -1,4 +1,6 @@ * Copyright (C) 2003 Xavier Dutoit * Copyright (C) 2004-2016 Laurent Destailleur @@ -37,7 +39,7 @@ class Conf public $file; /** - * @var DoliDB Database handler. + * @var Object Associative array with some properties ->type, ->db, ... */ public $db; From a78bc86465e35bc411b70012bb29e0d62f2fa3eb Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 20 May 2020 23:51:46 +0200 Subject: [PATCH 09/10] Trans --- htdocs/langs/en_US/accountancy.lang | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/htdocs/langs/en_US/accountancy.lang b/htdocs/langs/en_US/accountancy.lang index 92cc0f0c79e..fea7bf9d0ef 100644 --- a/htdocs/langs/en_US/accountancy.lang +++ b/htdocs/langs/en_US/accountancy.lang @@ -58,8 +58,8 @@ AccountancyAreaDescActionOnceBis=Next steps should be done to save you time in f AccountancyAreaDescActionFreq=The following actions are usually executed every month, week or day for very large companies... AccountancyAreaDescJournalSetup=STEP %s: Create or check content of your journal list from menu %s -AccountancyAreaDescChartModel=STEP %s: Create a model of chart of account from menu %s -AccountancyAreaDescChart=STEP %s: Create or check content of your chart of account from menu %s +AccountancyAreaDescChartModel=STEP %s: Check that a model of chart of account exists or create one from menu %s +AccountancyAreaDescChart=STEP %s: Select and|or complete your chart of account from menu %s AccountancyAreaDescVat=STEP %s: Define accounting accounts for each VAT Rates. For this, use the menu entry %s. AccountancyAreaDescDefault=STEP %s: Define default accounting accounts. For this, use the menu entry %s. From 9e3e8df60fa6260ae2581b8212a55c163ae7e6cb Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 20 May 2020 23:56:06 +0200 Subject: [PATCH 10/10] Fix phpcs --- htdocs/projet/class/project.class.php | 1576 ++++++++++++------------- 1 file changed, 788 insertions(+), 788 deletions(-) diff --git a/htdocs/projet/class/project.class.php b/htdocs/projet/class/project.class.php index 1e6c675d6fe..a5e1c197f8b 100644 --- a/htdocs/projet/class/project.class.php +++ b/htdocs/projet/class/project.class.php @@ -1215,875 +1215,875 @@ class Project extends CommonObject // $userAccess = 1; // } //} - } - - return ($userAccess ? $userAccess : -1); } - /** - * Return array of projects a user has permission on, is affected to, or all projects - * - * @param User $user User object - * @param int $mode 0=All project I have permission on (assigned to me or public), 1=Projects assigned to me only, 2=Will return list of all projects with no test on contacts - * @param int $list 0=Return array, 1=Return string list - * @param int $socid 0=No filter on third party, id of third party - * @param string $filter additionnal filter on project (statut, ref, ...) - * @return array or string Array of projects id, or string with projects id separated with "," if list is 1 - */ - public function getProjectsAuthorizedForUser($user, $mode = 0, $list = 0, $socid = 0, $filter = '') + return ($userAccess ? $userAccess : -1); + } + + /** + * Return array of projects a user has permission on, is affected to, or all projects + * + * @param User $user User object + * @param int $mode 0=All project I have permission on (assigned to me or public), 1=Projects assigned to me only, 2=Will return list of all projects with no test on contacts + * @param int $list 0=Return array, 1=Return string list + * @param int $socid 0=No filter on third party, id of third party + * @param string $filter additionnal filter on project (statut, ref, ...) + * @return array or string Array of projects id, or string with projects id separated with "," if list is 1 + */ + public function getProjectsAuthorizedForUser($user, $mode = 0, $list = 0, $socid = 0, $filter = '') + { + $projects = array(); + $temp = array(); + + $sql = "SELECT ".(($mode == 0 || $mode == 1) ? "DISTINCT " : "")."p.rowid, p.ref"; + $sql.= " FROM " . MAIN_DB_PREFIX . "projet as p"; + if ($mode == 0) { - $projects = array(); - $temp = array(); + $sql.= " LEFT JOIN " . MAIN_DB_PREFIX . "element_contact as ec ON ec.element_id = p.rowid"; + } + elseif ($mode == 1) + { + $sql.= ", " . MAIN_DB_PREFIX . "element_contact as ec"; + } + elseif ($mode == 2) + { + // No filter. Use this if user has permission to see all project + } + $sql.= " WHERE p.entity IN (".getEntity('project').")"; + // Internal users must see project he is contact to even if project linked to a third party he can't see. + //if ($socid || ! $user->rights->societe->client->voir) $sql.= " AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = ".$socid.")"; + if ($socid > 0) $sql.= " AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = " . $socid . ")"; - $sql = "SELECT ".(($mode == 0 || $mode == 1) ? "DISTINCT " : "")."p.rowid, p.ref"; - $sql.= " FROM " . MAIN_DB_PREFIX . "projet as p"; - if ($mode == 0) + // Get id of types of contacts for projects (This list never contains a lot of elements) + $listofprojectcontacttype=array(); + $sql2 = "SELECT ctc.rowid, ctc.code FROM ".MAIN_DB_PREFIX."c_type_contact as ctc"; + $sql2.= " WHERE ctc.element = '" . $this->db->escape($this->element) . "'"; + $sql2.= " AND ctc.source = 'internal'"; + $resql = $this->db->query($sql2); + if ($resql) + { + while($obj = $this->db->fetch_object($resql)) { - $sql.= " LEFT JOIN " . MAIN_DB_PREFIX . "element_contact as ec ON ec.element_id = p.rowid"; + $listofprojectcontacttype[$obj->rowid]=$obj->code; } - elseif ($mode == 1) - { - $sql.= ", " . MAIN_DB_PREFIX . "element_contact as ec"; - } - elseif ($mode == 2) - { - // No filter. Use this if user has permission to see all project - } - $sql.= " WHERE p.entity IN (".getEntity('project').")"; - // Internal users must see project he is contact to even if project linked to a third party he can't see. - //if ($socid || ! $user->rights->societe->client->voir) $sql.= " AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = ".$socid.")"; - if ($socid > 0) $sql.= " AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = " . $socid . ")"; + } + else dol_print_error($this->db); + if (count($listofprojectcontacttype) == 0) $listofprojectcontacttype[0]='0'; // To avoid syntax error if not found - // Get id of types of contacts for projects (This list never contains a lot of elements) - $listofprojectcontacttype=array(); - $sql2 = "SELECT ctc.rowid, ctc.code FROM ".MAIN_DB_PREFIX."c_type_contact as ctc"; - $sql2.= " WHERE ctc.element = '" . $this->db->escape($this->element) . "'"; - $sql2.= " AND ctc.source = 'internal'"; - $resql = $this->db->query($sql2); - if ($resql) - { - while($obj = $this->db->fetch_object($resql)) - { - $listofprojectcontacttype[$obj->rowid]=$obj->code; - } - } - else dol_print_error($this->db); - if (count($listofprojectcontacttype) == 0) $listofprojectcontacttype[0]='0'; // To avoid syntax error if not found - - if ($mode == 0) - { - $sql.= " AND ( p.public = 1"; - $sql.= " OR ( ec.fk_c_type_contact IN (".join(',', array_keys($listofprojectcontacttype)).")"; - $sql.= " AND ec.fk_socpeople = ".$user->id.")"; - $sql.= " )"; - } - elseif ($mode == 1) - { - $sql.= " AND ec.element_id = p.rowid"; - $sql.= " AND ("; - $sql.= " ( ec.fk_c_type_contact IN (".join(',', array_keys($listofprojectcontacttype)).")"; - $sql.= " AND ec.fk_socpeople = ".$user->id.")"; - $sql.= " )"; - } - elseif ($mode == 2) - { - // No filter. Use this if user has permission to see all project - } - - $sql.= $filter; - //print $sql; - - $resql = $this->db->query($sql); - if ($resql) - { - $num = $this->db->num_rows($resql); - $i = 0; - while ($i < $num) - { - $row = $this->db->fetch_row($resql); - $projects[$row[0]] = $row[1]; - $temp[] = $row[0]; - $i++; - } - - $this->db->free($resql); - - if ($list) - { - if (empty($temp)) return '0'; - $result = implode(',', $temp); - return $result; - } - } - else - { - dol_print_error($this->db); - } - - return $projects; + if ($mode == 0) + { + $sql.= " AND ( p.public = 1"; + $sql.= " OR ( ec.fk_c_type_contact IN (".join(',', array_keys($listofprojectcontacttype)).")"; + $sql.= " AND ec.fk_socpeople = ".$user->id.")"; + $sql.= " )"; + } + elseif ($mode == 1) + { + $sql.= " AND ec.element_id = p.rowid"; + $sql.= " AND ("; + $sql.= " ( ec.fk_c_type_contact IN (".join(',', array_keys($listofprojectcontacttype)).")"; + $sql.= " AND ec.fk_socpeople = ".$user->id.")"; + $sql.= " )"; + } + elseif ($mode == 2) + { + // No filter. Use this if user has permission to see all project } - /** - * Load an object from its id and create a new one in database - * - * @param User $user User making the clone - * @param int $fromid Id of object to clone - * @param bool $clone_contact Clone contact of project - * @param bool $clone_task Clone task of project - * @param bool $clone_project_file Clone file of project - * @param bool $clone_task_file Clone file of task (if task are copied) - * @param bool $clone_note Clone note of project - * @param bool $move_date Move task date on clone - * @param integer $notrigger No trigger flag - * @param int $newthirdpartyid New thirdparty id - * @return int New id of clone - */ - public function createFromClone(User $user, $fromid, $clone_contact = false, $clone_task = true, $clone_project_file = false, $clone_task_file = false, $clone_note = true, $move_date = true, $notrigger = 0, $newthirdpartyid = 0) + $sql.= $filter; + //print $sql; + + $resql = $this->db->query($sql); + if ($resql) { - global $langs, $conf; - - $error = 0; - - dol_syslog("createFromClone clone_contact=".$clone_contact." clone_task=".$clone_task." clone_project_file=".$clone_project_file." clone_note=".$clone_note." move_date=".$move_date, LOG_DEBUG); - - $now = dol_mktime(0, 0, 0, idate('m', dol_now()), idate('d', dol_now()), idate('Y', dol_now())); - - $clone_project = new Project($this->db); - - $clone_project->context['createfromclone'] = 'createfromclone'; - - $this->db->begin(); - - // Load source object - $clone_project->fetch($fromid); - $clone_project->fetch_optionals(); - if ($newthirdpartyid > 0) $clone_project->socid = $newthirdpartyid; - $clone_project->fetch_thirdparty(); - - $orign_dt_start = $clone_project->date_start; - $orign_project_ref = $clone_project->ref; - - $clone_project->id = 0; - if ($move_date) { - $clone_project->date_start = $now; - if (!(empty($clone_project->date_end))) - { - $clone_project->date_end = $clone_project->date_end + ($now - $orign_dt_start); - } + $num = $this->db->num_rows($resql); + $i = 0; + while ($i < $num) + { + $row = $this->db->fetch_row($resql); + $projects[$row[0]] = $row[1]; + $temp[] = $row[0]; + $i++; } - $clone_project->datec = $now; + $this->db->free($resql); + if ($list) + { + if (empty($temp)) return '0'; + $result = implode(',', $temp); + return $result; + } + } + else + { + dol_print_error($this->db); + } + + return $projects; + } + + /** + * Load an object from its id and create a new one in database + * + * @param User $user User making the clone + * @param int $fromid Id of object to clone + * @param bool $clone_contact Clone contact of project + * @param bool $clone_task Clone task of project + * @param bool $clone_project_file Clone file of project + * @param bool $clone_task_file Clone file of task (if task are copied) + * @param bool $clone_note Clone note of project + * @param bool $move_date Move task date on clone + * @param integer $notrigger No trigger flag + * @param int $newthirdpartyid New thirdparty id + * @return int New id of clone + */ + public function createFromClone(User $user, $fromid, $clone_contact = false, $clone_task = true, $clone_project_file = false, $clone_task_file = false, $clone_note = true, $move_date = true, $notrigger = 0, $newthirdpartyid = 0) + { + global $langs, $conf; + + $error = 0; + + dol_syslog("createFromClone clone_contact=".$clone_contact." clone_task=".$clone_task." clone_project_file=".$clone_project_file." clone_note=".$clone_note." move_date=".$move_date, LOG_DEBUG); + + $now = dol_mktime(0, 0, 0, idate('m', dol_now()), idate('d', dol_now()), idate('Y', dol_now())); + + $clone_project = new Project($this->db); + + $clone_project->context['createfromclone'] = 'createfromclone'; + + $this->db->begin(); + + // Load source object + $clone_project->fetch($fromid); + $clone_project->fetch_optionals(); + if ($newthirdpartyid > 0) $clone_project->socid = $newthirdpartyid; + $clone_project->fetch_thirdparty(); + + $orign_dt_start = $clone_project->date_start; + $orign_project_ref = $clone_project->ref; + + $clone_project->id = 0; + if ($move_date) { + $clone_project->date_start = $now; + if (!(empty($clone_project->date_end))) + { + $clone_project->date_end = $clone_project->date_end + ($now - $orign_dt_start); + } + } + + $clone_project->datec = $now; + + if (!$clone_note) + { + $clone_project->note_private = ''; + $clone_project->note_public = ''; + } + + //Generate next ref + $defaultref = ''; + $obj = empty($conf->global->PROJECT_ADDON) ? 'mod_project_simple' : $conf->global->PROJECT_ADDON; + // Search template files + $file = ''; $classname = ''; $filefound = 0; + $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']); + foreach ($dirmodels as $reldir) + { + $file = dol_buildpath($reldir."core/modules/project/".$obj.'.php', 0); + if (file_exists($file)) + { + $filefound = 1; + dol_include_once($reldir."core/modules/project/".$obj.'.php'); + $modProject = new $obj; + $defaultref = $modProject->getNextValue(is_object($clone_project->thirdparty) ? $clone_project->thirdparty : null, $clone_project); + break; + } + } + if (is_numeric($defaultref) && $defaultref <= 0) $defaultref = ''; + + $clone_project->ref = $defaultref; + $clone_project->title = $langs->trans("CopyOf").' '.$clone_project->title; + + // Create clone + $result = $clone_project->create($user, $notrigger); + + // Other options + if ($result < 0) + { + $this->error .= $clone_project->error; + $error++; + } + + if (!$error) + { + //Get the new project id + $clone_project_id = $clone_project->id; + + //Note Update if (!$clone_note) { $clone_project->note_private = ''; $clone_project->note_public = ''; } - - //Generate next ref - $defaultref = ''; - $obj = empty($conf->global->PROJECT_ADDON) ? 'mod_project_simple' : $conf->global->PROJECT_ADDON; - // Search template files - $file = ''; $classname = ''; $filefound = 0; - $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']); - foreach ($dirmodels as $reldir) + else { - $file = dol_buildpath($reldir."core/modules/project/".$obj.'.php', 0); - if (file_exists($file)) + $this->db->begin(); + $res = $clone_project->update_note(dol_html_entity_decode($clone_project->note_public, ENT_QUOTES), '_public'); + if ($res < 0) { - $filefound = 1; - dol_include_once($reldir."core/modules/project/".$obj.'.php'); - $modProject = new $obj; - $defaultref = $modProject->getNextValue(is_object($clone_project->thirdparty) ? $clone_project->thirdparty : null, $clone_project); - break; - } - } - if (is_numeric($defaultref) && $defaultref <= 0) $defaultref = ''; - - $clone_project->ref = $defaultref; - $clone_project->title = $langs->trans("CopyOf").' '.$clone_project->title; - - // Create clone - $result = $clone_project->create($user, $notrigger); - - // Other options - if ($result < 0) - { - $this->error .= $clone_project->error; - $error++; - } - - if (!$error) - { - //Get the new project id - $clone_project_id = $clone_project->id; - - //Note Update - if (!$clone_note) - { - $clone_project->note_private = ''; - $clone_project->note_public = ''; + $this->error .= $clone_project->error; + $error++; + $this->db->rollback(); } else { - $this->db->begin(); - $res = $clone_project->update_note(dol_html_entity_decode($clone_project->note_public, ENT_QUOTES), '_public'); - if ($res < 0) - { - $this->error .= $clone_project->error; - $error++; - $this->db->rollback(); - } - else - { - $this->db->commit(); - } - - $this->db->begin(); - $res = $clone_project->update_note(dol_html_entity_decode($clone_project->note_private, ENT_QUOTES), '_private'); - if ($res < 0) - { - $this->error .= $clone_project->error; - $error++; - $this->db->rollback(); - } - else - { - $this->db->commit(); - } + $this->db->commit(); } - //Duplicate contact - if ($clone_contact) + $this->db->begin(); + $res = $clone_project->update_note(dol_html_entity_decode($clone_project->note_private, ENT_QUOTES), '_private'); + if ($res < 0) { - $origin_project = new Project($this->db); - $origin_project->fetch($fromid); - - foreach (array('internal', 'external') as $source) - { - $tab = $origin_project->liste_contact(-1, $source); - - foreach ($tab as $contacttoadd) - { - $clone_project->add_contact($contacttoadd['id'], $contacttoadd['code'], $contacttoadd['source'], $notrigger); - if ($clone_project->error == 'DB_ERROR_RECORD_ALREADY_EXISTS') - { - $langs->load("errors"); - $this->error .= $langs->trans("ErrorThisContactIsAlreadyDefinedAsThisType"); - $error++; - } - else - { - if ($clone_project->error != '') - { - $this->error .= $clone_project->error; - $error++; - } - } - } - } + $this->error .= $clone_project->error; + $error++; + $this->db->rollback(); } - - //Duplicate file - if ($clone_project_file) + else { - require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; - - $clone_project_dir = $conf->projet->dir_output."/".dol_sanitizeFileName($defaultref); - $ori_project_dir = $conf->projet->dir_output."/".dol_sanitizeFileName($orign_project_ref); - - if (dol_mkdir($clone_project_dir) >= 0) - { - $filearray = dol_dir_list($ori_project_dir, "files", 0, '', '(\.meta|_preview.*\.png)$', '', SORT_ASC, 1); - foreach ($filearray as $key => $file) - { - $rescopy = dol_copy($ori_project_dir.'/'.$file['name'], $clone_project_dir.'/'.$file['name'], 0, 1); - if (is_numeric($rescopy) && $rescopy < 0) - { - $this->error .= $langs->trans("ErrorFailToCopyFile", $ori_project_dir.'/'.$file['name'], $clone_project_dir.'/'.$file['name']); - $error++; - } - } - } - else - { - $this->error .= $langs->trans('ErrorInternalErrorDetected').':dol_mkdir'; - $error++; - } + $this->db->commit(); } + } - //Duplicate task - if ($clone_task) + //Duplicate contact + if ($clone_contact) + { + $origin_project = new Project($this->db); + $origin_project->fetch($fromid); + + foreach (array('internal', 'external') as $source) { - require_once DOL_DOCUMENT_ROOT . '/projet/class/task.class.php'; + $tab = $origin_project->liste_contact(-1, $source); - $taskstatic = new Task($this->db); - - // Security check - $socid=0; - if ($user->socid > 0) $socid = $user->socid; - - $tasksarray=$taskstatic->getTasksArray(0, 0, $fromid, $socid, 0); - - $tab_conv_child_parent=array(); - - // Loop on each task, to clone it - foreach ($tasksarray as $tasktoclone) + foreach ($tab as $contacttoadd) { - $result_clone = $taskstatic->createFromClone($user, $tasktoclone->id, $clone_project_id, $tasktoclone->fk_parent, $move_date, true, false, $clone_task_file, true, false); - if ($result_clone <= 0) + $clone_project->add_contact($contacttoadd['id'], $contacttoadd['code'], $contacttoadd['source'], $notrigger); + if ($clone_project->error == 'DB_ERROR_RECORD_ALREADY_EXISTS') { - $this->error .= $result_clone->error; + $langs->load("errors"); + $this->error .= $langs->trans("ErrorThisContactIsAlreadyDefinedAsThisType"); $error++; } else { - $new_task_id = $result_clone; - $taskstatic->fetch($tasktoclone->id); - - //manage new parent clone task id - // if the current task has child we store the original task id and the equivalent clone task id - if (($taskstatic->hasChildren()) && !array_key_exists($tasktoclone->id, $tab_conv_child_parent)) + if ($clone_project->error != '') { - $tab_conv_child_parent[$tasktoclone->id] = $new_task_id; + $this->error .= $clone_project->error; + $error++; } } } + } + } - //Parse all clone node to be sure to update new parent - $tasksarray = $taskstatic->getTasksArray(0, 0, $clone_project_id, $socid, 0); - foreach ($tasksarray as $task_cloned) + //Duplicate file + if ($clone_project_file) + { + require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; + + $clone_project_dir = $conf->projet->dir_output."/".dol_sanitizeFileName($defaultref); + $ori_project_dir = $conf->projet->dir_output."/".dol_sanitizeFileName($orign_project_ref); + + if (dol_mkdir($clone_project_dir) >= 0) + { + $filearray = dol_dir_list($ori_project_dir, "files", 0, '', '(\.meta|_preview.*\.png)$', '', SORT_ASC, 1); + foreach ($filearray as $key => $file) { - $taskstatic->fetch($task_cloned->id); - if ($taskstatic->fk_task_parent != 0) + $rescopy = dol_copy($ori_project_dir.'/'.$file['name'], $clone_project_dir.'/'.$file['name'], 0, 1); + if (is_numeric($rescopy) && $rescopy < 0) { - $taskstatic->fk_task_parent = $tab_conv_child_parent[$taskstatic->fk_task_parent]; - } - $res = $taskstatic->update($user, $notrigger); - if ($result_clone <= 0) - { - $this->error .= $taskstatic->error; + $this->error .= $langs->trans("ErrorFailToCopyFile", $ori_project_dir.'/'.$file['name'], $clone_project_dir.'/'.$file['name']); $error++; } } } - } - - unset($clone_project->context['createfromclone']); - - if (!$error) - { - $this->db->commit(); - return $clone_project_id; - } - else - { - $this->db->rollback(); - dol_syslog(get_class($this)."::createFromClone nbError: ".$error." error : ".$this->error, LOG_ERR); - return -1; - } - } - - - /** - * Shift project task date from current date to delta - * - * @param integer $old_project_dt_start Old project start date - * @return int 1 if OK or < 0 if KO - */ - public function shiftTaskDate($old_project_dt_start) - { - global $user, $langs, $conf; - - $error=0; - - $taskstatic = new Task($this->db); - - // Security check - $socid=0; - if ($user->socid > 0) $socid = $user->socid; - - $tasksarray=$taskstatic->getTasksArray(0, 0, $this->id, $socid, 0); - - foreach ($tasksarray as $tasktoshiftdate) - { - $to_update=false; - // Fetch only if update of date will be made - if ((!empty($tasktoshiftdate->date_start)) || (!empty($tasktoshiftdate->date_end))) + else { - //dol_syslog(get_class($this)."::shiftTaskDate to_update", LOG_DEBUG); - $to_update=true; - $task = new Task($this->db); - $result = $task->fetch($tasktoshiftdate->id); - if (!$result) + $this->error .= $langs->trans('ErrorInternalErrorDetected').':dol_mkdir'; + $error++; + } + } + + //Duplicate task + if ($clone_task) + { + require_once DOL_DOCUMENT_ROOT . '/projet/class/task.class.php'; + + $taskstatic = new Task($this->db); + + // Security check + $socid=0; + if ($user->socid > 0) $socid = $user->socid; + + $tasksarray=$taskstatic->getTasksArray(0, 0, $fromid, $socid, 0); + + $tab_conv_child_parent=array(); + + // Loop on each task, to clone it + foreach ($tasksarray as $tasktoclone) + { + $result_clone = $taskstatic->createFromClone($user, $tasktoclone->id, $clone_project_id, $tasktoclone->fk_parent, $move_date, true, false, $clone_task_file, true, false); + if ($result_clone <= 0) { + $this->error .= $result_clone->error; $error++; - $this->error.=$task->error; - } - } - //print "$this->date_start + $tasktoshiftdate->date_start - $old_project_dt_start";exit; - - //Calcultate new task start date with difference between old proj start date and origin task start date - if (!empty($tasktoshiftdate->date_start)) - { - $task->date_start = $this->date_start + ($tasktoshiftdate->date_start - $old_project_dt_start); - } - - //Calcultate new task end date with difference between origin proj end date and origin task end date - if (!empty($tasktoshiftdate->date_end)) - { - $task->date_end = $this->date_start + ($tasktoshiftdate->date_end - $old_project_dt_start); - } - - if ($to_update) - { - $result = $task->update($user); - if (!$result) - { - $error++; - $this->error .= $task->error; - } - } - } - if ($error != 0) - { - return -1; - } - return $result; - } - - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Associate element to a project - * - * @param string $tableName Table of the element to update - * @param int $elementSelectId Key-rowid of the line of the element to update - * @return int 1 if OK or < 0 if KO - */ - public function update_element($tableName, $elementSelectId) - { - // phpcs:enable - $sql = "UPDATE ".MAIN_DB_PREFIX.$tableName; - - if ($tableName == "actioncomm") - { - $sql .= " SET fk_project=".$this->id; - $sql .= " WHERE id=".$elementSelectId; - } - else - { - $sql .= " SET fk_projet=".$this->id; - $sql .= " WHERE rowid=".$elementSelectId; - } - - dol_syslog(get_class($this)."::update_element", LOG_DEBUG); - $resql = $this->db->query($sql); - if (!$resql) { - $this->error = $this->db->lasterror(); - return -1; - } else { - return 1; - } - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Associate element to a project - * - * @param string $tableName Table of the element to update - * @param int $elementSelectId Key-rowid of the line of the element to update - * @param string $projectfield The column name that stores the link with the project - * - * @return int 1 if OK or < 0 if KO - */ - public function remove_element($tableName, $elementSelectId, $projectfield = 'fk_projet') - { - // phpcs:enable - $sql = "UPDATE ".MAIN_DB_PREFIX.$tableName; - - if ($tableName == "actioncomm") - { - $sql .= " SET fk_project=NULL"; - $sql .= " WHERE id=".$elementSelectId; - } else - { - $sql .= " SET ".$projectfield."=NULL"; - $sql .= " WHERE rowid=".$elementSelectId; - } - - dol_syslog(get_class($this)."::remove_element", LOG_DEBUG); - $resql = $this->db->query($sql); - if (!$resql) { - $this->error = $this->db->lasterror(); - return -1; - } else { - return 1; - } - } - - /** - * Create an intervention document on disk using template defined into PROJECT_ADDON_PDF - * - * @param string $modele Force template to use ('' by default) - * @param Translate $outputlangs Objet lang to use for translation - * @param int $hidedetails Hide details of lines - * @param int $hidedesc Hide description - * @param int $hideref Hide ref - * @return int 0 if KO, 1 if OK - */ - public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0) - { - global $conf,$langs; - - $langs->load("projects"); - - if (! dol_strlen($modele)) { - $modele = 'baleine'; - - if ($this->modelpdf) { - $modele = $this->modelpdf; - } elseif (! empty($conf->global->PROJECT_ADDON_PDF)) { - $modele = $conf->global->PROJECT_ADDON_PDF; - } - } - - $modelpath = "core/modules/project/doc/"; - - return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref); - } - - - /** - * Load time spent into this->weekWorkLoad and this->weekWorkLoadPerTask for all day of a week of project. - * Note: array weekWorkLoad and weekWorkLoadPerTask are reset and filled at each call. - * - * @param int $datestart First day of week (use dol_get_first_day to find this date) - * @param int $taskid Filter on a task id - * @param int $userid Time spent by a particular user - * @return int <0 if OK, >0 if KO - */ - public function loadTimeSpent($datestart, $taskid = 0, $userid = 0) - { - $error=0; - - $this->weekWorkLoad=array(); - $this->weekWorkLoadPerTask=array(); - - if (empty($datestart)) 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.= " AND pt.fk_projet = ".$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)."')"; - if ($taskid) $sql.= " AND ptt.fk_task=".$taskid; - if (is_numeric($userid)) $sql.= " AND ptt.fk_user=".$userid; - - //print $sql; - $resql=$this->db->query($sql); - if ($resql) - { - $daylareadyfound=array(); - - $num = $this->db->num_rows($resql); - $i = 0; - // 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 - if (empty($daylareadyfound[$day])) - { - $this->weekWorkLoad[$day] = $obj->task_duration; - $this->weekWorkLoadPerTask[$day][$obj->fk_task] = $obj->task_duration; } else { - $this->weekWorkLoad[$day] += $obj->task_duration; - $this->weekWorkLoadPerTask[$day][$obj->fk_task] += $obj->task_duration; - } - $daylareadyfound[$day]=1; - $i++; - } - $this->db->free($resql); - return 1; - } - else - { - $this->error = "Error ".$this->db->lasterror(); - dol_syslog(get_class($this)."::fetch ".$this->error, LOG_ERR); - return -1; - } - } + $new_task_id = $result_clone; + $taskstatic->fetch($tasktoclone->id); - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Load indicators for dashboard (this->nbtodo and this->nbtodolate) - * - * @param User $user Objet user - * @return WorkboardResponse|int <0 if KO, WorkboardResponse if OK - */ - public function load_board($user) - { - // phpcs:enable - global $conf, $langs; - - // For external user, no check is done on company because readability is managed by public status of project and assignement. - //$socid=$user->socid; - - $projectsListId = null; - if (!$user->rights->projet->all->lire) $projectsListId = $this->getProjectsAuthorizedForUser($user, 0, 1); - - $sql = "SELECT p.rowid, p.fk_statut as status, p.fk_opp_status, p.datee as datee"; - $sql .= " FROM (".MAIN_DB_PREFIX."projet as p"; - $sql .= ")"; - $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s on p.fk_soc = s.rowid"; - // For external user, no check is done on company permission because readability is managed by public status of project and assignement. - //if (! $user->rights->societe->client->voir && ! $socid) $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON sc.fk_soc = s.rowid"; - $sql .= " WHERE p.fk_statut = 1"; - $sql .= " AND p.entity IN (".getEntity('project').')'; - if (!empty($projectsListId)) $sql .= " AND p.rowid IN (".$projectsListId.")"; - // No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser - //if ($socid || ! $user->rights->societe->client->voir) $sql.= " AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = ".$socid.")"; - // For external user, no check is done on company permission because readability is managed by public status of project and assignement. - //if (! $user->rights->societe->client->voir && ! $socid) $sql.= " AND ((s.rowid = sc.fk_soc AND sc.fk_user = " .$user->id.") OR (s.rowid IS NULL))"; - - //print $sql; - $resql = $this->db->query($sql); - if ($resql) - { - $project_static = new Project($this->db); - - $response = new WorkboardResponse(); - $response->warning_delay = $conf->projet->warning_delay / 60 / 60 / 24; - $response->label = $langs->trans("OpenedProjects"); - $response->labelShort = $langs->trans("Opened"); - if ($user->rights->projet->all->lire) $response->url = DOL_URL_ROOT.'/projet/list.php?search_status=1&mainmenu=project'; - else $response->url = DOL_URL_ROOT.'/projet/list.php?search_project_user=-1&search_status=1&mainmenu=project'; - $response->img = img_object('', "projectpub"); - - // This assignment in condition is not a bug. It allows walking the results. - while ($obj = $this->db->fetch_object($resql)) - { - $response->nbtodo++; - - $project_static->statut = $obj->status; - $project_static->opp_status = $obj->opp_status; - $project_static->datee = $this->db->jdate($obj->datee); - - if ($project_static->hasDelay()) { - $response->nbtodolate++; + //manage new parent clone task id + // if the current task has child we store the original task id and the equivalent clone task id + if (($taskstatic->hasChildren()) && !array_key_exists($tasktoclone->id, $tab_conv_child_parent)) + { + $tab_conv_child_parent[$tasktoclone->id] = $new_task_id; + } } } - return $response; - } - else - { - $this->error = $this->db->error(); - return -1; - } - } - - - /** - * Function used to replace a thirdparty id with another one. - * - * @param DoliDB $db Database handler - * @param int $origin_id Old thirdparty id - * @param int $dest_id New thirdparty id - * @return bool - */ - public static function replaceThirdparty(DoliDB $db, $origin_id, $dest_id) - { - $tables = array( - 'projet' - ); - - return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables); - } - - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Charge indicateurs this->nb pour le tableau de bord - * - * @return int <0 if KO, >0 if OK - */ - public function load_state_board() - { - // phpcs:enable - global $user; - - $this->nb = array(); - - $sql = "SELECT count(p.rowid) as nb"; - $sql .= " FROM ".MAIN_DB_PREFIX."projet as p"; - $sql .= " WHERE"; - $sql .= " p.entity IN (".getEntity('project').")"; - if (!$user->rights->projet->all->lire) - { - $projectsListId = $this->getProjectsAuthorizedForUser($user, 0, 1); - $sql .= "AND p.rowid IN (".$projectsListId.")"; - } - - $resql = $this->db->query($sql); - if ($resql) - { - while ($obj = $this->db->fetch_object($resql)) + //Parse all clone node to be sure to update new parent + $tasksarray = $taskstatic->getTasksArray(0, 0, $clone_project_id, $socid, 0); + foreach ($tasksarray as $task_cloned) { - $this->nb["projects"] = $obj->nb; - } - $this->db->free($resql); - return 1; - } - else - { - dol_print_error($this->db); - $this->error = $this->db->error(); - return -1; - } - } - - - /** - * Is the project delayed? - * - * @return bool - */ - public function hasDelay() - { - global $conf; - - if (!($this->statut == self::STATUS_VALIDATED)) return false; - if (!$this->datee && !$this->date_end) return false; - - $now = dol_now(); - - return ($this->datee ? $this->datee : $this->date_end) < ($now - $conf->projet->warning_delay); - } - - - /** - * Charge les informations d'ordre info dans l'objet commande - * - * @param int $id Id of order - * @return void - */ - public function info($id) - { - $sql = 'SELECT c.rowid, datec as datec, tms as datem,'; - $sql .= ' date_close as datecloture,'; - $sql .= ' fk_user_creat as fk_user_author, fk_user_close as fk_use_cloture'; - $sql .= ' FROM '.MAIN_DB_PREFIX.'projet as c'; - $sql .= ' WHERE c.rowid = '.$id; - $result = $this->db->query($sql); - if ($result) - { - if ($this->db->num_rows($result)) - { - $obj = $this->db->fetch_object($result); - $this->id = $obj->rowid; - if ($obj->fk_user_author) + $taskstatic->fetch($task_cloned->id); + if ($taskstatic->fk_task_parent != 0) { - $cuser = new User($this->db); - $cuser->fetch($obj->fk_user_author); - $this->user_creation = $cuser; + $taskstatic->fk_task_parent = $tab_conv_child_parent[$taskstatic->fk_task_parent]; } - - if ($obj->fk_user_cloture) + $res = $taskstatic->update($user, $notrigger); + if ($result_clone <= 0) { - $cluser = new User($this->db); - $cluser->fetch($obj->fk_user_cloture); - $this->user_cloture = $cluser; + $this->error .= $taskstatic->error; + $error++; } - - $this->date_creation = $this->db->jdate($obj->datec); - $this->date_modification = $this->db->jdate($obj->datem); - $this->date_cloture = $this->db->jdate($obj->datecloture); } - - $this->db->free($result); - } - else - { - dol_print_error($this->db); } } - /** - * Sets object to supplied categories. - * - * Deletes object from existing categories not supplied. - * Adds it to non existing supplied categories. - * Existing categories are left untouch. - * - * @param int[]|int $categories Category or categories IDs - * @return void - */ - public function setCategories($categories) + unset($clone_project->context['createfromclone']); + + if (!$error) { - $type_categ = Categorie::TYPE_PROJECT; + $this->db->commit(); + return $clone_project_id; + } + else + { + $this->db->rollback(); + dol_syslog(get_class($this)."::createFromClone nbError: ".$error." error : ".$this->error, LOG_ERR); + return -1; + } + } - // Handle single category - if (!is_array($categories)) { - $categories = array($categories); - } - // Get current categories - require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php'; - $c = new Categorie($this->db); - $existing = $c->containing($this->id, $type_categ, 'id'); + /** + * Shift project task date from current date to delta + * + * @param integer $old_project_dt_start Old project start date + * @return int 1 if OK or < 0 if KO + */ + public function shiftTaskDate($old_project_dt_start) + { + global $user, $langs, $conf; - // Diff - if (is_array($existing)) { - $to_del = array_diff($existing, $categories); - $to_add = array_diff($categories, $existing); - } else { - $to_del = array(); // Nothing to delete - $to_add = $categories; - } + $error=0; - // Process - foreach ($to_del as $del) { - if ($c->fetch($del) > 0) { - $result = $c->del_type($this, $type_categ); - if ($result < 0) { - $this->errors = $c->errors; - $this->error = $c->error; - return -1; - } + $taskstatic = new Task($this->db); + + // Security check + $socid=0; + if ($user->socid > 0) $socid = $user->socid; + + $tasksarray=$taskstatic->getTasksArray(0, 0, $this->id, $socid, 0); + + foreach ($tasksarray as $tasktoshiftdate) + { + $to_update=false; + // Fetch only if update of date will be made + if ((!empty($tasktoshiftdate->date_start)) || (!empty($tasktoshiftdate->date_end))) + { + //dol_syslog(get_class($this)."::shiftTaskDate to_update", LOG_DEBUG); + $to_update=true; + $task = new Task($this->db); + $result = $task->fetch($tasktoshiftdate->id); + if (!$result) + { + $error++; + $this->error.=$task->error; } } - foreach ($to_add as $add) { - if ($c->fetch($add) > 0) { - $result = $c->add_type($this, $type_categ); - if ($result < 0) { - $this->errors = $c->errors; - $this->error = $c->error; - return -1; - } - } + //print "$this->date_start + $tasktoshiftdate->date_start - $old_project_dt_start";exit; + + //Calcultate new task start date with difference between old proj start date and origin task start date + if (!empty($tasktoshiftdate->date_start)) + { + $task->date_start = $this->date_start + ($tasktoshiftdate->date_start - $old_project_dt_start); } + //Calcultate new task end date with difference between origin proj end date and origin task end date + if (!empty($tasktoshiftdate->date_end)) + { + $task->date_end = $this->date_start + ($tasktoshiftdate->date_end - $old_project_dt_start); + } + + if ($to_update) + { + $result = $task->update($user); + if (!$result) + { + $error++; + $this->error .= $task->error; + } + } + } + if ($error != 0) + { + return -1; + } + return $result; + } + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Associate element to a project + * + * @param string $tableName Table of the element to update + * @param int $elementSelectId Key-rowid of the line of the element to update + * @return int 1 if OK or < 0 if KO + */ + public function update_element($tableName, $elementSelectId) + { + // phpcs:enable + $sql = "UPDATE ".MAIN_DB_PREFIX.$tableName; + + if ($tableName == "actioncomm") + { + $sql .= " SET fk_project=".$this->id; + $sql .= " WHERE id=".$elementSelectId; + } + else + { + $sql .= " SET fk_projet=".$this->id; + $sql .= " WHERE rowid=".$elementSelectId; + } + + dol_syslog(get_class($this)."::update_element", LOG_DEBUG); + $resql = $this->db->query($sql); + if (!$resql) { + $this->error = $this->db->lasterror(); + return -1; + } else { return 1; } + } + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Associate element to a project + * + * @param string $tableName Table of the element to update + * @param int $elementSelectId Key-rowid of the line of the element to update + * @param string $projectfield The column name that stores the link with the project + * + * @return int 1 if OK or < 0 if KO + */ + public function remove_element($tableName, $elementSelectId, $projectfield = 'fk_projet') + { + // phpcs:enable + $sql = "UPDATE ".MAIN_DB_PREFIX.$tableName; - /** - * Create an array of tasks of current project - * - * @param User $user Object user we want project allowed to - * @return int >0 if OK, <0 if KO - */ - public function getLinesArray($user) + if ($tableName == "actioncomm") { - require_once DOL_DOCUMENT_ROOT.'/projet/class/task.class.php'; - $taskstatic = new Task($this->db); - - $this->lines = $taskstatic->getTasksArray(0, $user, $this->id, 0, 0); + $sql .= " SET fk_project=NULL"; + $sql .= " WHERE id=".$elementSelectId; + } else + { + $sql .= " SET ".$projectfield."=NULL"; + $sql .= " WHERE rowid=".$elementSelectId; } + + dol_syslog(get_class($this)."::remove_element", LOG_DEBUG); + $resql = $this->db->query($sql); + if (!$resql) { + $this->error = $this->db->lasterror(); + return -1; + } else { + return 1; + } + } + + /** + * Create an intervention document on disk using template defined into PROJECT_ADDON_PDF + * + * @param string $modele Force template to use ('' by default) + * @param Translate $outputlangs Objet lang to use for translation + * @param int $hidedetails Hide details of lines + * @param int $hidedesc Hide description + * @param int $hideref Hide ref + * @return int 0 if KO, 1 if OK + */ + public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0) + { + global $conf,$langs; + + $langs->load("projects"); + + if (! dol_strlen($modele)) { + $modele = 'baleine'; + + if ($this->modelpdf) { + $modele = $this->modelpdf; + } elseif (! empty($conf->global->PROJECT_ADDON_PDF)) { + $modele = $conf->global->PROJECT_ADDON_PDF; + } + } + + $modelpath = "core/modules/project/doc/"; + + return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref); + } + + + /** + * Load time spent into this->weekWorkLoad and this->weekWorkLoadPerTask for all day of a week of project. + * Note: array weekWorkLoad and weekWorkLoadPerTask are reset and filled at each call. + * + * @param int $datestart First day of week (use dol_get_first_day to find this date) + * @param int $taskid Filter on a task id + * @param int $userid Time spent by a particular user + * @return int <0 if OK, >0 if KO + */ + public function loadTimeSpent($datestart, $taskid = 0, $userid = 0) + { + $error=0; + + $this->weekWorkLoad=array(); + $this->weekWorkLoadPerTask=array(); + + if (empty($datestart)) 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.= " AND pt.fk_projet = ".$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)."')"; + if ($taskid) $sql.= " AND ptt.fk_task=".$taskid; + if (is_numeric($userid)) $sql.= " AND ptt.fk_user=".$userid; + + //print $sql; + $resql=$this->db->query($sql); + if ($resql) + { + $daylareadyfound=array(); + + $num = $this->db->num_rows($resql); + $i = 0; + // 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 + if (empty($daylareadyfound[$day])) + { + $this->weekWorkLoad[$day] = $obj->task_duration; + $this->weekWorkLoadPerTask[$day][$obj->fk_task] = $obj->task_duration; + } + else + { + $this->weekWorkLoad[$day] += $obj->task_duration; + $this->weekWorkLoadPerTask[$day][$obj->fk_task] += $obj->task_duration; + } + $daylareadyfound[$day]=1; + $i++; + } + $this->db->free($resql); + return 1; + } + else + { + $this->error = "Error ".$this->db->lasterror(); + dol_syslog(get_class($this)."::fetch ".$this->error, LOG_ERR); + return -1; + } + } + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Load indicators for dashboard (this->nbtodo and this->nbtodolate) + * + * @param User $user Objet user + * @return WorkboardResponse|int <0 if KO, WorkboardResponse if OK + */ + public function load_board($user) + { + // phpcs:enable + global $conf, $langs; + + // For external user, no check is done on company because readability is managed by public status of project and assignement. + //$socid=$user->socid; + + $projectsListId = null; + if (!$user->rights->projet->all->lire) $projectsListId = $this->getProjectsAuthorizedForUser($user, 0, 1); + + $sql = "SELECT p.rowid, p.fk_statut as status, p.fk_opp_status, p.datee as datee"; + $sql .= " FROM (".MAIN_DB_PREFIX."projet as p"; + $sql .= ")"; + $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s on p.fk_soc = s.rowid"; + // For external user, no check is done on company permission because readability is managed by public status of project and assignement. + //if (! $user->rights->societe->client->voir && ! $socid) $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON sc.fk_soc = s.rowid"; + $sql .= " WHERE p.fk_statut = 1"; + $sql .= " AND p.entity IN (".getEntity('project').')'; + if (!empty($projectsListId)) $sql .= " AND p.rowid IN (".$projectsListId.")"; + // No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser + //if ($socid || ! $user->rights->societe->client->voir) $sql.= " AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = ".$socid.")"; + // For external user, no check is done on company permission because readability is managed by public status of project and assignement. + //if (! $user->rights->societe->client->voir && ! $socid) $sql.= " AND ((s.rowid = sc.fk_soc AND sc.fk_user = " .$user->id.") OR (s.rowid IS NULL))"; + + //print $sql; + $resql = $this->db->query($sql); + if ($resql) + { + $project_static = new Project($this->db); + + $response = new WorkboardResponse(); + $response->warning_delay = $conf->projet->warning_delay / 60 / 60 / 24; + $response->label = $langs->trans("OpenedProjects"); + $response->labelShort = $langs->trans("Opened"); + if ($user->rights->projet->all->lire) $response->url = DOL_URL_ROOT.'/projet/list.php?search_status=1&mainmenu=project'; + else $response->url = DOL_URL_ROOT.'/projet/list.php?search_project_user=-1&search_status=1&mainmenu=project'; + $response->img = img_object('', "projectpub"); + + // This assignment in condition is not a bug. It allows walking the results. + while ($obj = $this->db->fetch_object($resql)) + { + $response->nbtodo++; + + $project_static->statut = $obj->status; + $project_static->opp_status = $obj->opp_status; + $project_static->datee = $this->db->jdate($obj->datee); + + if ($project_static->hasDelay()) { + $response->nbtodolate++; + } + } + + return $response; + } + else + { + $this->error = $this->db->error(); + return -1; + } + } + + + /** + * Function used to replace a thirdparty id with another one. + * + * @param DoliDB $db Database handler + * @param int $origin_id Old thirdparty id + * @param int $dest_id New thirdparty id + * @return bool + */ + public static function replaceThirdparty(DoliDB $db, $origin_id, $dest_id) + { + $tables = array( + 'projet' + ); + + return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables); + } + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Charge indicateurs this->nb pour le tableau de bord + * + * @return int <0 if KO, >0 if OK + */ + public function load_state_board() + { + // phpcs:enable + global $user; + + $this->nb = array(); + + $sql = "SELECT count(p.rowid) as nb"; + $sql .= " FROM ".MAIN_DB_PREFIX."projet as p"; + $sql .= " WHERE"; + $sql .= " p.entity IN (".getEntity('project').")"; + if (!$user->rights->projet->all->lire) + { + $projectsListId = $this->getProjectsAuthorizedForUser($user, 0, 1); + $sql .= "AND p.rowid IN (".$projectsListId.")"; + } + + $resql = $this->db->query($sql); + if ($resql) + { + while ($obj = $this->db->fetch_object($resql)) + { + $this->nb["projects"] = $obj->nb; + } + $this->db->free($resql); + return 1; + } + else + { + dol_print_error($this->db); + $this->error = $this->db->error(); + return -1; + } + } + + + /** + * Is the project delayed? + * + * @return bool + */ + public function hasDelay() + { + global $conf; + + if (!($this->statut == self::STATUS_VALIDATED)) return false; + if (!$this->datee && !$this->date_end) return false; + + $now = dol_now(); + + return ($this->datee ? $this->datee : $this->date_end) < ($now - $conf->projet->warning_delay); + } + + + /** + * Charge les informations d'ordre info dans l'objet commande + * + * @param int $id Id of order + * @return void + */ + public function info($id) + { + $sql = 'SELECT c.rowid, datec as datec, tms as datem,'; + $sql .= ' date_close as datecloture,'; + $sql .= ' fk_user_creat as fk_user_author, fk_user_close as fk_use_cloture'; + $sql .= ' FROM '.MAIN_DB_PREFIX.'projet as c'; + $sql .= ' WHERE c.rowid = '.$id; + $result = $this->db->query($sql); + if ($result) + { + if ($this->db->num_rows($result)) + { + $obj = $this->db->fetch_object($result); + $this->id = $obj->rowid; + if ($obj->fk_user_author) + { + $cuser = new User($this->db); + $cuser->fetch($obj->fk_user_author); + $this->user_creation = $cuser; + } + + if ($obj->fk_user_cloture) + { + $cluser = new User($this->db); + $cluser->fetch($obj->fk_user_cloture); + $this->user_cloture = $cluser; + } + + $this->date_creation = $this->db->jdate($obj->datec); + $this->date_modification = $this->db->jdate($obj->datem); + $this->date_cloture = $this->db->jdate($obj->datecloture); + } + + $this->db->free($result); + } + else + { + dol_print_error($this->db); + } + } + + /** + * Sets object to supplied categories. + * + * Deletes object from existing categories not supplied. + * Adds it to non existing supplied categories. + * Existing categories are left untouch. + * + * @param int[]|int $categories Category or categories IDs + * @return void + */ + public function setCategories($categories) + { + $type_categ = Categorie::TYPE_PROJECT; + + // Handle single category + if (!is_array($categories)) { + $categories = array($categories); + } + + // Get current categories + require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php'; + $c = new Categorie($this->db); + $existing = $c->containing($this->id, $type_categ, 'id'); + + // Diff + if (is_array($existing)) { + $to_del = array_diff($existing, $categories); + $to_add = array_diff($categories, $existing); + } else { + $to_del = array(); // Nothing to delete + $to_add = $categories; + } + + // Process + foreach ($to_del as $del) { + if ($c->fetch($del) > 0) { + $result = $c->del_type($this, $type_categ); + if ($result < 0) { + $this->errors = $c->errors; + $this->error = $c->error; + return -1; + } + } + } + foreach ($to_add as $add) { + if ($c->fetch($add) > 0) { + $result = $c->add_type($this, $type_categ); + if ($result < 0) { + $this->errors = $c->errors; + $this->error = $c->error; + return -1; + } + } + } + + return 1; + } + + + /** + * Create an array of tasks of current project + * + * @param User $user Object user we want project allowed to + * @return int >0 if OK, <0 if KO + */ + public function getLinesArray($user) + { + require_once DOL_DOCUMENT_ROOT.'/projet/class/task.class.php'; + $taskstatic = new Task($this->db); + + $this->lines = $taskstatic->getTasksArray(0, $user, $this->id, 0, 0); + } }