. * * You can get complete code of ProjeQtOr, other resource, help and information * about contributors at http://www.projeqtor.org * *** DO NOT REMOVE THIS NOTICE ************************************************/ /* ============================================================================ * Planning element is an object included in all objects that can be planned. */ require_once('_securityCheck.php'); class PlanningElement extends SqlElement { public $id; public $idProject; public $refType; public $refId; public $refName; public $initialStartDate; public $validatedStartDate; public $validatedStartFraction; public $plannedStartDate; public $plannedStartFraction; public $realStartDate; public $initialEndDate; public $validatedEndDate; public $validatedEndFraction; public $plannedEndDate; public $plannedEndFraction; public $realEndDate; public $initialDuration; public $validatedDuration; public $plannedDuration; public $realDuration; public $initialWork; public $validatedWork; public $assignedWork; public $plannedWork; public $leftWork; public $realWork; public $progress; public $validatedCost; public $assignedCost; public $plannedCost; public $leftCost; public $realCost; public $expectedProgress; public $wbs; public $wbsSortable; public $topId; public $topRefType; public $topRefId; public $priority; public $elementary; public $idle; public $done; public $cancelled; public $idPlanningMode; public $_workVisibility; public $_costVisibility; public $idBill; public $validatedCalculated; public $validatedExpenseCalculated; public $notPlannedWork; private static $_fieldsAttributes=array( "id"=>"hidden", "refType"=>"hidden", "refId"=>"hidden", "refName"=>"hidden", "wbs"=>"display,noImport", "wbsSortable"=>"hidden,noImport", "progress"=>"display,noImport", "expectedProgress"=>"display,noImport", "marginWorkPct"=>"display,noImport", "marginCostPct"=>"display,noImport", "marginWork"=>"readonly,noImport", "marginCost"=>"readonly,noImport", "topType"=>"hidden", "topId"=>"hidden", "topRefType"=>"hidden", "topRefId"=>"hidden", "idProject"=>"hidden", "idle"=>"hidden", "done"=>"hidden", "cancelled"=>"hidden", "plannedStartDate"=>"readonly,noImport", "plannedEndDate"=>"readonly,noImport", "plannedDuration"=>"readonly,noImport", "plannedWork"=>"readonly,noImport", "notPlannedWork"=>"hidden", "realStartDate"=>"readonly,noImport", "realEndDate"=>"readonly,noImport", "realDuration"=>"readonly,noImport", "realWork"=>"readonly,noImport", "assignedCost"=>"readonly,noImport", "realCost"=>"readonly,noImport", "leftCost"=>"readonly,noImport", "validatedCost"=>"", "plannedCost"=>"readonly,noImport", "elementary"=>"hidden", "idPlanningMode"=>"hidden", "idBill"=>"hidden", "validatedCalculated"=>"hidden", "validatedExpenseCalculated"=>"hidden", "plannedStartFraction"=>"hidden", "plannedEndFraction"=>"hidden", "validatedStartFraction"=>"hidden", "validatedEndFraction"=>"hidden" ); private static $predecessorItemsArray = array(); private static $staticCostVisibility=null; private static $staticWorkVisibility=null; public static $_noDispatch=false; public static $_noDispatchArray=array(); public static $_copiedItems=array(); /** ========================================================================== * Constructor * @param $id the id of the object in the database (null if not stored yet) * @return void */ function __construct($id = NULL, $withoutDependentObjects=false) { parent::__construct($id,$withoutDependentObjects); } /** ========================================================================== * Destructor * @return void */ function __destruct() { parent::__destruct(); } // ============================================================================********** // GET VALIDATION SCRIPT // ============================================================================********** /** ========================================================================== * Return the validation sript for some fields * @return the validation javascript (for dojo frameword) */ public function getValidationScript($colName) { $colScript = parent::getValidationScript($colName); $rubr=""; $name=""; $test = 'initial'; $pos = stripos( $colName, $test); if ($pos!==false) { $rubr=$test; $name=substr($colName,$pos+strlen($test)); } else { $test = 'validated'; $pos = stripos( $colName, $test); if ($pos!==false) { $rubr=$test; $name=substr($colName,$pos+strlen($test)); } else { $test = 'planned'; $pos = stripos( $colName, $test); if ($pos!==false) { $rubr=$test; $name=substr($colName,$pos+strlen($test)); } else { $test = 'real'; $pos = stripos( $colName, $test); if ($pos!==false) { $rubr=$test; $name=substr($colName,$pos+strlen($test)); } } } } if ($name=="StartDate") { $colScript .= ''; } else if ($name=="EndDate") { // Not to do any more for end date (not managed this way) ???? Reactivted ! $colScript .= ''; } else if ($name=="Duration") { $colScript .= ''; } return $colScript; } /** ========================================================================== * Extends save functionality to implement wbs calculation * Triggers parent::save() to run defaut functionality in the end. * @return the result of parent::save() function */ public function save() { // Get old element (stored in database) : must be fetched before saving $old=new PlanningElement($this->id); if (! $this->idProject) { if ($this->refType=='Project') { $this->idProject=$this->refId; } else if ($this->refType) { $refObj=new $this->refType($this->refId); $this->idProject=$refObj->idProject; } } if (! $this->idProject and $this->refType=='Project') { $this->idProject=$this->refId; } // If done and no work, set up end date if ( $this->leftWork==0 and $this->realWork==0) { $refType=$this->refType; if ($refType) { $refObj=new $refType($this->refId); if ($this->done and property_exists($refObj, 'doneDate')) { $this->realEndDate=$refObj->doneDate; $this->progress=100; $this->expectedProgress=100; } else { $this->realEndDate=null; $this->progress=0; $this->expectedProgress=0; } if (property_exists($refObj, 'handled') and property_exists($refObj, 'handledDate')) { if ($refObj->handled) { $this->realStartDate=$refObj->handledDate; } else { $this->realStartDate=null; } } } } else { $this->progress = round($this->realWork / ($this->realWork + $this->leftWork) * 100); } if ($this->validatedWork!=0) { $this->expectedProgress=round($this->realWork / ($this->validatedWork) *100); if ($this->expectedProgress>999999) { $this->expectedProgress=999999; } } else { if (!$this->expectedProgress) { $this->expectedProgress=0; } } // update topId if needed $topElt=null; if ( (! $this->topId or trim($this->topId)=='') and ( $this->topRefId and trim($this->topRefId)!='') ) { $crit=array("refType"=>$this->topRefType, "refId"=>$this->topRefId); $topElt=SqlElement::getSingleSqlElementFromCriteria('PlanningElement',$crit); if ($topElt) { $this->topId=$topElt->id; $topElt->elementary=0; } } // calculate wbs $dispatchNeeded=false; //$wbs=""; $crit=''; if (! $this->wbs or trim($this->wbs)=='') { $wbs=""; //if ( $this->topId and trim($this->topId)!='') { if ($topElt) { //$elt=new PlanningElement($this->topId); $wbs=$topElt->wbs . "."; $crit=" topId=" . Sql::fmtId($this->topId); } else { $crit=" (topId is null) "; } if ($this->id) { $crit.=" and id<>" . Sql::fmtId($this->id); } $lst=$this->getSqlElementsFromCriteria(null, null, $crit, 'wbsSortable desc'); if (count($lst)==0) { $localSort=1; } else { if ( !$lst[0]->wbsSortable or $lst[0]->wbsSortable=='') { $localSort=1; } else { $localSort=substr($lst[0]->wbsSortable,-3,3)+1; } } $wbs.=$localSort; $this->wbs=$wbs; $dispatchNeeded=true; } $wbsSortable=formatSortableWbs($this->wbs); if ($wbsSortable != $this->wbsSortable) { $dispatchNeeded=true; } $this->wbsSortable=$wbsSortable; // search for dependant elements $crit=" topId=" . Sql::fmtId($this->id); $this->elementary=1; $lstElt=$this->getSqlElementsFromCriteria(null, null, $crit ,'wbsSortable asc'); if ($lstElt and count($lstElt)>0) { $this->elementary=0; } else { $this->elementary=1; $this->validatedCalculated=0; $this->validatedExpenseCalculated=0; } if (! $this->priority or $this->priority==0) { $this->priority=500; // default value for priority } $this->realDuration=workDayDiffDates($this->realStartDate, $this->realEndDate); $this->plannedDuration=workDayDiffDates($this->plannedStartDate, $this->plannedEndDate); //if (!$this->plannedDuration and $this->validatedDuration) { // Initialize planned duration to validated //$this->plannedDuration=$this->validatedDuration; //if ($this->plannedStartDate) { // $this->plannedEndDate=addWorkDaysToDate($this->plannedStartDate, $this->plannedDuration); //} //} if ($this->validatedStartDate and $this->validatedEndDate) { $this->validatedDuration=workDayDiffDates($this->validatedStartDate, $this->validatedEndDate); } if ($this->initialStartDate and $this->initialEndDate) { $this->initialDuration=workDayDiffDates($this->initialStartDate, $this->initialEndDate); } // $consolidateValidated=Parameter::getGlobalParameter('consolidateValidated'); if ($consolidateValidated=='NO' or ! $consolidateValidated) { $this->validatedCalculated=0; $this->validatedExpenseCalculated=0; } else if ($consolidateValidated=='ALWAYS' and ! $this->elementary) { $this->validatedCalculated=1; } $result=parent::save(); if (! strpos($result,'id="lastOperationStatus" value="OK"')) { return $result; } // Update dependant objects if ($dispatchNeeded and ! self::$_noDispatch) { projeqtor_set_time_limit(600); $cpt=0; foreach ($lstElt as $elt) { $cpt++; $elt->wbs=$this->wbs . '.' . $cpt; if ($elt->refType) { // just security for unit testing $elt->wbsSave(); } } } // update topObject if ($topElt) { if ($topElt->refId) { if (! self::$_noDispatch) { $topElt->save(); } else { if ($this->elementary) { // noDispatch (for copy) and elementary : store top in array for updateSynthesis self::$_noDispatchArray[$topElt->id]=$topElt; } } } } if ($this->topId!=$old->topId) // save old parent (for synthesis update) if parent has changed if ($old->topId!='' and $old->topId!=$this->topId and ! self::$_noDispatch) { $this->updateSynthesis($old->topRefType, $old->topRefId); } // save new parent (for synthesis update) if parent has changed if ($this->topId!='' and ! self::$_noDispatch) { // and ($old->topId!=$this->topId or $old->cancelled!=$this->cancelled)) { $this->updateSynthesis($this->topRefType, $this->topRefId); } if ($this->wbsSortable!=$old->wbsSortable ) { $refType=$this->refType; if ($refType=='Project') { $refObj=new $refType($this->refId); $refObj->sortOrder=$this->wbsSortable; $subRes=$refObj->saveForced(true); } } // remove existing planned work (if any) if ($this->idle) { $pw=new PlannedWork(); $crit="refType=".Sql::str($this->refType)." and refId=".$this->refId; $pw->purge($crit); } // set to first handled status on first work input //if ($old->realWork==0 and $this->realWork!=0 and $this->refType) { if ($old->realWork==0 and $this->realWork!=0 and $this->refType) { $this->setHandledOnRealWork('save'); } // set to first done status on lastt work input (left work = 0) if ($old->leftWork!=0 and $this->leftWork==0 and $this->realWork>0 and $this->refType) { $this->setDoneOnNoLeftWork('save'); } if ($old->topId!=$this->topId and ! self::$_noDispatch) { // This renumbering is to avoid holes in numbering $pe=new PlanningElement($old->topId); $pe->renumberWbs(); } return $result; } public function setHandledOnRealWork ($action='check') { $refType=$this->refType; $refObj=new $refType($this->refId); $newStatus=null; if (property_exists($refObj, 'idStatus') and Parameter::getGlobalParameter('setHandledOnRealWork')=='YES') { $st=new Status($refObj->idStatus); if (!$st->setHandledStatus) { // if current stauts is not handled, move to first allowed handled status (fitting workflow) $typeClass=$refType.'Type'; $typeField='id'.$typeClass; $type=new $typeClass($refObj->$typeField); $user=getSessionUser(); // Is change possible ? if (property_exists($type, 'mandatoryResourceOnHandled') and $type->mandatoryResourceOnHandled) { // Resource Mandatroy if (property_exists($refObj, 'idResource') and ! $refObj->idResource) { // Resource not set if (! $user->isResource or Parameter::getGlobalParameter('setResponsibleIfNeeded')=='NO') { // Resource will not be set // So, cannot change status to handled (responsible needed) return '[noResource]'; } } } $crit=array('idWorkflow'=>$type->idWorkflow, 'idStatusFrom'=>$refObj->idStatus, 'idProfile'=>$user->getProfile($this->idProject), 'allowed'=>'1'); $ws=new WorkflowStatus(); $possibleStatus=$ws->getSqlElementsFromCriteria($crit); $in="(0"; foreach ($possibleStatus as $ws) { $in.=",".$ws->idStatusTo; } $in.=")"; $st=new Status(); $stList=$st->getSqlElementsFromCriteria(null, null, " setHandledStatus=1 and id in ".$in, 'sortOrder asc'); if (count($stList)>0) { if ($action=='save') { $refObj->idStatus=$stList[0]->id; $resSetStatus=$refObj->save(); } return $stList[0]->name; // Return new status name } } } return null; // OK nothing to do } public function setDoneOnNoLeftWork($action='check', $simulatedStartStatus=null) { $refType=$this->refType; $refObj=new $refType($this->refId); if (property_exists($refObj, 'idStatus') and Parameter::getGlobalParameter('setDoneOnNoLeftWork')=='YES') { $st=null; if ($simulatedStartStatus) { $st=new Status(SqlList::getIdFromName('Status', $simulatedStartStatus)); } if (! $st or !$st->id) { $st=new Status($refObj->idStatus); } if (!$st->setDoneStatus) { // if current status is not handled, move to first allowed handled status (fitting workflow) $typeClass=$refType.'Type'; $typeField='id'.$typeClass; $type=new $typeClass($refObj->$typeField); $user=getSessionUser(); // Is change possible ? if (property_exists($type, 'mandatoryResultOnDone') and $type->mandatoryResultOnDone) { // Result Mandatroy if (property_exists($refObj, 'result') and !$refObj->result) { // Result not set // So, cannot change status to done (result needed) return '[noResult]'; } } $crit=array('idWorkflow'=>$type->idWorkflow, 'idStatusFrom'=>$refObj->idStatus, 'idProfile'=>$user->getProfile($this->idProject), 'allowed'=>'1'); $ws=new WorkflowStatus(); $possibleStatus=$ws->getSqlElementsFromCriteria($crit); $in="(0"; foreach ($possibleStatus as $ws) { $in.=",".$ws->idStatusTo; } $in.=")"; $st=new Status(); $stList=$st->getSqlElementsFromCriteria(null, null, " setDoneStatus=1 and id in ".$in, 'sortOrder asc'); if (count($stList)>0) { if ($action=='save') { $refObj->idStatus=$stList[0]->id; $resSetStatus=$refObj->save(); } return $stList[0]->name; } } } return null; // OK nothing to do } public function simpleSave() { $this->plannedDuration=workDayDiffDates($this->plannedStartDate, $this->plannedEndDate); if ($this->validatedStartDate and $this->validatedEndDate) { $this->validatedDuration=workDayDiffDates($this->validatedStartDate, $this->validatedEndDate); } if ($this->initialStartDate and $this->initialEndDate) { $this->initialDuration=workDayDiffDates($this->initialStartDate, $this->initialEndDate); } $result = parent::save(); } public function wbsSave() { // $this->_noHistory=true; $this->wbsSortable=formatSortableWbs($this->wbs); $this->saveForced(); if ($this->refType=='Project') { $proj=new Project($this->refId); $proj->sortOrder=$this->wbsSortable; $proj->saveForced(); } $crit=" topId=" . Sql::fmtId($this->id); $lstElt=$this->getSqlElementsFromCriteria(null, null, $crit ,'wbsSortable asc'); $cpt=0; foreach ($lstElt as $elt) { $cpt++; $elt->wbs=$this->wbs . '.' . $cpt; if ($elt->refType) { // just security for unit testing $elt->wbsSave(); } } } /** ========================================================================== * Return the specific fieldsAttributes * @return the fieldsAttributes */ protected function getStaticFieldsAttributes() { return self::$_fieldsAttributes; } /** ========================================================================= * Update the synthesis Data (work). * Called by sub-element (assignment, ...) * @param $col the nale of the property * @return a boolean */ protected function updateSynthesisObj ($doNotSave=false) { $consolidateValidated=Parameter::getGlobalParameter('consolidateValidated'); $this->validatedCalculated=0; $this->validatedExpenseCalculated=0; $assignedWork=0; $leftWork=0; $plannedWork=0; $notPlannedWork=0; $realWork=0; $validatedWork=0; $assignedCost=0; $leftCost=0; $plannedCost=0; $realCost=0; $validatedCost=0; $validatedExpense=0; //$this->_noHistory=true; // Should keep history of changes $this->_workHistory=true; // History will be tagged in order to select visibility // Add data from assignments directly linked to this item $critAss=array("refType"=>$this->refType, "refId"=>$this->refId); $assignment=new Assignment(); $assList=$assignment->getSqlElementsFromCriteria($critAss, false); if ($this->refType=='PeriodicMeeting') { $assList=array(); } $realStartDate=null; $realEndDate=null; $plannedStartDate=null; $plannedEndDate=null; foreach ($assList as $ass) { $assignedWork+=$ass->assignedWork; $leftWork+=$ass->leftWork; $plannedWork+=$ass->plannedWork; $notPlannedWork+=$ass->notPlannedWork; $realWork+=$ass->realWork; if ($ass->assignedCost) $assignedCost+=$ass->assignedCost; if ($ass->leftCost) $leftCost+=$ass->leftCost; if ($ass->plannedCost) $plannedCost+=$ass->plannedCost; if ($ass->realCost) $realCost+=$ass->realCost; if ( $ass->realStartDate and (! $realStartDate or $ass->realStartDate<$realStartDate )) { $realStartDate=$ass->realStartDate; } if ( $ass->realEndDate and (! $realEndDate or $ass->realEndDate>$realEndDate )) { $realEndDate=$ass->realEndDate; } if ( $ass->plannedStartDate and (! $plannedStartDate or $ass->plannedStartDate<$plannedStartDate )) { $plannedStartDate=$ass->plannedStartDate; } if ( $ass->plannedEndDate and (! $plannedEndDate or $ass->plannedEndDate>$plannedEndDate )) { $plannedEndDate=$ass->plannedEndDate; } } // Add data from other planningElements dependant from this one if (! $this->elementary) { $critPla=array("topId"=>$this->id); $planningElement=new PlanningElement(); $plaList=$planningElement->getSqlElementsFromCriteria($critPla, false); // Add data from other planningElements dependant from this one foreach ($plaList as $pla) { $assignedWork+=$pla->assignedWork; $leftWork+=$pla->leftWork; $plannedWork+=$pla->plannedWork; $notPlannedWork+=$pla->notPlannedWork; $realWork+=$pla->realWork; if (!$pla->cancelled and $pla->assignedCost) $assignedCost+=$pla->assignedCost; if (!$pla->cancelled and $pla->leftCost) $leftCost+=$pla->leftCost; if ($pla->plannedCost) $plannedCost+=$pla->plannedCost; if ($pla->realCost) $realCost+=$pla->realCost; if ( !$pla->cancelled and $pla->realStartDate and (! $realStartDate or $pla->realStartDate<$realStartDate )) { $realStartDate=$pla->realStartDate; } if ( !$pla->cancelled and $pla->realEndDate and (! $realEndDate or $pla->realEndDate>$realEndDate )) { $realEndDate=$pla->realEndDate; } if ( !$pla->cancelled and $pla->plannedStartDate and (! $plannedStartDate or $pla->plannedStartDate<$plannedStartDate )) { $plannedStartDate=$pla->plannedStartDate; } if ( !$pla->cancelled and $pla->plannedEndDate and (! $plannedEndDate or $pla->plannedEndDate>$plannedEndDate )) { $plannedEndDate=$pla->plannedEndDate; } // If realEnd calculated, but left task with no work, keep real not set if ($realEndDate and !$pla->realEndDate and $pla->assignedWork==0 and $pla->leftWork==0 and $pla->plannedEndDate>$realEndDate) { $realEndDate=""; } if (!$pla->cancelled and $pla->validatedWork) $validatedWork+=$pla->validatedWork; if (!$pla->cancelled and $pla->validatedCost) $validatedCost+=$pla->validatedCost; } } $this->realStartDate=$realStartDate; if ($realWork>0 or $leftWork>0) { if ($leftWork==0) { $this->realEndDate=$realEndDate; } else { $this->realEndDate=null; } } if ($plannedStartDate) {$this->plannedStartDate=$plannedStartDate;} if ($this->elementary and $plannedStartDate and $realStartDate and $realStartDate<$plannedStartDate) { $this->plannedStartDate=$realStartDate; } if ($plannedEndDate) {$this->plannedEndDate=$plannedEndDate;} // save cumulated data $this->assignedWork=$assignedWork; $this->leftWork=$leftWork; $this->plannedWork=$plannedWork; $this->notPlannedWork=$notPlannedWork; $this->realWork=$realWork; $this->assignedCost=$assignedCost; $this->leftCost=$leftCost; $this->plannedCost=$plannedCost; $this->realCost=$realCost; if ($consolidateValidated=="ALWAYS") { $this->validatedWork=$validatedWork; $this->validatedCost=$validatedCost; $this->validatedCalculated=1; } else if ($consolidateValidated=="IFSET") { if ($validatedWork) { $this->validatedWork=$validatedWork; $this->validatedCalculated=1; } if ($validatedCost) { $this->validatedCost=$validatedCost; $this->validatedCalculated=1; } } if (! $doNotSave) { $this->save(); // Dispath to top element if ($this->topId) { self::updateSynthesis($this->topRefType, $this->topRefId); } } } /** ========================================================================= * Update the synthesis Data (work). * Called by sub-element (assignment, ...) * @param $col the nale of the property * @return a boolean */ public static function updateSynthesis ($refType, $refId) { $crit=array("refType"=>$refType, "refId"=>$refId); $obj=SqlElement::getSingleSqlElementFromCriteria($refType.'PlanningElement', $crit); if (! $obj or ! $obj->id) { $obj=SqlElement::getSingleSqlElementFromCriteria('PlanningElement', $crit); } if ($obj) { $method='updateSynthesis'.$refType; if (method_exists($obj,$method )) { return $obj->$method(); } else { return $obj->updateSynthesisObj(); } } } /** * Delete object * @see persistence/SqlElement#save() */ public function delete() { $refType=$this->topRefType; $refId=$this->topRefId; $result = parent::delete(); if (! strpos($result,'id="lastOperationStatus" value="OK"')) { return $result; } $topElt=null; if ( $refId and trim($refId)!='') { $crit=array("refType"=>$refType, "refId"=>$refId); $topElt=SqlElement::getSingleSqlElementFromCriteria('PlanningElement',$crit); if ($topElt and $topElt->id) { if ($topElt->refId) { $topElt->save(); } self::updateSynthesis($refType, $refId); } } if ($this->topId) { // This renumbering is to avoid holes in numbering $pe=new PlanningElement($this->topId); $pe->renumberWbs(); } // Dispatch value return $result; } /** ========================================================================= * control data corresponding to Model constraints * @param void * @return "OK" if controls are good or an error message * must be redefined in the inherited class */ public function control(){ $result=""; if ($this->idle and $this->leftWork>0) { $result.='
' . i18n('errorIdleWithLeftWork'); } $stat=array('initial','validated','planned','real'); foreach ($stat as $st) { $start=$st.'StartDate'; $end=$st.'EndDate'; $startAttr=$this->getFieldAttributes($start); $endAttr=$this->getFieldAttributes($end); if (strpos($startAttr,'hidden')===false and strpos($startAttr,'readonly')===false and strpos($endAttr,'hidden')===false and strpos($endAttr,'readonly')===false ) { if ($this->$start and $this->$end and $this->$start>$this->$end) { $result.='
' . i18n('errorStartEndDates',array($this->getColCaption($start),$this->getColCaption($end))); } } } $defaultControl=parent::control(); if ($defaultControl!='OK') { $result.=$defaultControl; } if ($result=="") { $result='OK'; } return $result; } public function deleteControl() { $result=""; // Cannot delete item with real work if ($this->id and $this->realWork and $this->realWork>0) { $result .= "
" . i18n("msgUnableToDeleteRealWork"); } if (! $result) { $result=parent::deleteControl(); } return $result; } public function controlHierarchicLoop($parentType, $parentId) { $result=""; $parent=SqlElement::getSingleSqlElementFromCriteria('PlanningElement',array('refType'=>$parentType,'refId'=>$parentId)); $parentList=$parent->getParentItemsArray(); if (array_key_exists('#' . $this->id,$parentList)) { $result='
' . i18n('errorHierarchicLoop'); return $result; } $precListObj=$this->getPredecessorItemsArray(); $succListObj=$this->getSuccessorItemsArray(); $parentListObj=$parent->getParentItemsArray(); $parentListObj['#'.$parent->id]=$parent; foreach ($parentListObj as $parentId=>$parentObj) { if (array_key_exists($parentId, $precListObj)) { $result='
' . i18n('errorHierarchicLoop'); return $result; } if (array_key_exists($parentId, $succListObj)) { $result='
' . i18n('errorHierarchicLoop'); return $result; } } return $result; } public function getParentItemsArray() { // V2.1 refactoring of function $result=array(); if ($this->topId) { $parent=new PlanningElement($this->topId); $result=$parent->getParentItemsArray(); $result['#' . $parent->id]=$parent; } return $result; } /** ============================================================== * Retrieve the list of all Predecessors, recursively */ public function getPredecessorItemsArray() { // Imporvement : get static stored value if already fetched /*if (array_key_exists('#' . $this->id, self::$predecessorItemsArray)) { return self::$predecessorItemsArray['#' . $this->id]; }*/ $result=array(); $crit=array("successorId"=>$this->id); $dep=new Dependency(); $depList=$dep->getSqlElementsFromCriteria($crit, false); foreach ($depList as $dep) { $elt=new PlanningElement($dep->predecessorId); if ($elt->id and ! array_key_exists('#' . $elt->id, $result)) { $result['#' . $elt->id]=$elt; $resultPredecessor=$elt->getPredecessorItemsArray(); $result=array_merge($result,$resultPredecessor); } } // Imporvement : static store result to avoid multiple fetch //self::$predecessorItemsArray['#' . $this->id]=$result; return $result; } /** ============================================================== * Retrieve the list of direct Predecessors, and may include direct parents predecessors */ public static function getPredecessorList($idCurrent, $includeParents=false) { $dep=new Dependency(); if (! $includeParents) { return $dep->getSqlElementsFromCriteria(array("successorId"=>$idCurrent),false); } // Include parents successsors $testParent=new PlanningElement($idCurrent); $resultList=$dep->getSqlElementsFromCriteria(array("successorId"=>$idCurrent),false,null, null, true); while ($testParent->topId) { $testParent=new PlanningElement($testParent->topId); $list=$dep->getSqlElementsFromCriteria(array("successorId"=>$testParent->id),false,null, null, true); $resultList=array_merge($resultList,$list); } return $resultList; } public function getPredecessorItemsArrayIncludingParents() { $result=$this->getPredecessorItemsArray(); $parents=$this->getParentItemsArray(); foreach ($parents as $parent) { $resParent=$parent->getPredecessorItemsArray(); array_merge($result,$resParent); } return $result; } /** ============================================================== * Retrieve the list of all Successors, recursively */ public function getSuccessorItemsArray() { $result=array(); $crit=array("predecessorId"=>$this->id); $dep=new Dependency(); $depList=$dep->getSqlElementsFromCriteria($crit, false); foreach ($depList as $dep) { $elt=new PlanningElement($dep->successorId); if ($elt->id and ! array_key_exists('#' . $elt->id, $result)) { $result['#' . $elt->id]=$elt; $resultSuccessor=$elt->getSuccessorItemsArray(); $result=array_merge($result,$resultSuccessor); } } return $result; } public function moveTo($destId,$mode,$recursive=false) { $status="ERROR"; $result=""; $returnValue=""; $task=null; $checkClass=get_class($this); if (SqlElement::is_a($this, 'PlanningElement')) { $checkClass=$this->refType; } $right=securityGetAccessRightYesNo('menu' . $checkClass, 'update', $this); if ($right!='YES') { $returnValue=i18n('errorUpdateRights'); $returnValue .= ''; $returnValue .= ''; $returnValue .= ''; return $returnValue; } $dest=new PlanningElement($destId); if ($dest->topRefType!=$this->topRefType or $dest->topRefId!=$this->topRefId) { $objectClass=$this->refType; $objectId=$this->refId; $task=new $objectClass($objectId); if ($dest->topRefType=="Project") { $task->idProject=$dest->topRefId; if (property_exists($task, 'idActivity')) { $task->idActivity=null; } $status="OK"; } else if ($dest->topRefType=="Activity" and property_exists($task, 'idActivity')) { $task->idProject=$dest->idProject; $task->idActivity=$dest->topRefId; $status="OK"; } else if (! $dest->topRefType and $objectClass=='Project') { $task->idProject=null; $status="OK"; } if ($status=="OK") { //$task->save(); //$this->__construct($this->id); //$result=i18n('moveDone'); } else { $returnValue=i18n('moveCancelled'); } } if (! $returnValue) { if ($this->topRefType) { $where="topRefType='" . $this->topRefType . "' and topRefId=" . Sql::fmtId($this->topRefId) ; } else { $where="topRefType is null and topRefId is null"; } $order="wbsSortable asc"; $list=$this->getSqlElementsFromCriteria(null,false,$where,$order); $idx=0; $currentIdx=0; foreach ($list as $pe) { if ($pe->id==$this->id) { // met the one we are moving => skip } else { if ($pe->id==$destId and $mode=="before") { $idx++; $currentIdx=$idx; } $idx++; $root=substr($pe->wbs,0,strrpos($pe->wbs,'.')); $pe->wbs=($root=='')?$idx:$root.'.'.$idx; if ($pe->refType) { $pe->save(); } if ($pe->id==$destId and $mode=="after") { $idx++; $currentIdx=$idx; } } } $root=substr($this->wbs,0,strrpos($this->wbs,'.')); $this->wbs=($root=='')?$currentIdx:$root.'.'.$currentIdx; $this->save(); $returnValue=i18n('moveDone'); $status="OK"; } if ($status=="OK" and $task and !$recursive) { $resultTask=$task->save(); if (stripos($resultTask,'id="lastOperationStatus" value="OK"')>0 ) { $pe=new PlanningElement($this->id); $pe->moveTo($destId,$mode,true); $returnValue=i18n('moveDone'); } else { $returnValue=$resultTask;//i18n('moveCancelled'); $status="ERROR"; } } $returnValue .= ''; $returnValue .= ''; $returnValue .= ''; return $returnValue; } public function indent($way) { $result=i18n('moveCancelled'); $status="ERROR"; $objectClass=$this->refType; $objectId=$this->refId; $task=new $objectClass($objectId); if ($way=="decrease") { $top=null; if (property_exists($task, 'idActivity') and $task->idActivity) { $top=new Activity($task->idActivity); } else if (property_exists($task, 'idProject') and $task->idProject) { $top=new Project($task->idProject); } if ($top and property_exists($top, 'idActivity') and $top->idActivity) { $task->idActivity=$top->idActivity; $task->save(); $result=i18n('moveDone'); $status="OK"; } else if ($top and property_exists($top, 'idProject') and ($top->idProject or $objectClass=='Project') ) { if (property_exists($task, 'idActivity') and $task->idActivity) { $task->idActivity=null; } $task->idProject=$top->idProject; $task->save(); $result=i18n('moveDone'); $status="OK"; } if ($top and $status=="OK") { $pe=new PlanningElement($this->id); $crit=array('refType'=>get_class($top),'refId'=>$top->id); $peTop=SqlElement::getSingleSqlElementFromCriteria('PlanningElement', $crit); echo $pe->moveTo($peTop->id,"after"); } } else { // $way=="increase" $precs=$this->getSqlElementsFromCriteria(null,false, "wbsSortable<'".$this->wbsSortable."' and idProject in " . getVisibleProjectsList(true),"wbsSortable desc"); if (count($precs)>0) { foreach ($precs as $pp) { if (strlen($pp->wbsSortable)<=strlen($this->wbsSortable)) { $proj=new Project($pp->idProject); $type=new Type($proj->idProjectType); if ($type->code=='TMP' or $type->code=='ADM') { continue; } else { $prec=$pp; break; } } } if ($prec->refType=='Project' and $prec->refId!=$task->idProject) { $task->idProject=$prec->refId; $task->save(); $result=i18n('moveDone'); $status="OK"; } else if ($prec->refType=='Activity' and property_exists($task, 'idActivity') and $task->idActivity!=$prec->refId) { $task->idActivity=$prec->refId; $task->save(); $result=i18n('moveDone'); $status="OK"; } else { // Cannot move } } } $result .= ''; $result .= ''; $result .= ''; return $result; } public function renumberWbs() { if ($this->id) { $where="topRefType='" . $this->refType . "' and topRefId=" . Sql::fmtId($this->refId) ; } else { $where="refType is null and refId is null"; } $order="wbsSortable asc"; $list=$this->getSqlElementsFromCriteria(null,false,$where,$order); $idx=0; $currentIdx=0; foreach ($list as $pe) { $idx++; $root=substr($pe->wbs,0,strrpos($pe->wbs,'.')); $pe->wbs=($root=='')?$idx:$root.'.'.$idx; if ($pe->refType) { $pe->save(); } } } public static function getWorkVisibiliy($profile) { if (! self::$staticWorkVisibility or ! isset(self::$staticWorkVisibility[$profile]) ) { $pe=new PlanningElement(); $pe->setVisibility($profile); } return self::$staticWorkVisibility[$profile]; } public static function getCostVisibiliy($profile) { if (! self::$staticCostVisibility or ! isset(self::$staticCostVisibility[$profile]) ) { $pe=new PlanningElement(); $pe->setVisibility($profile); } return self::$staticCostVisibility[$profile]; } public function setVisibility($profile=null) { if (! sessionUserExists()) { return; } if (! $profile) { $user=getSessionUser(); $profile=$user->getProfile($this->idProject); } if (self::$staticCostVisibility and isset(self::$staticCostVisibility[$profile]) and self::$staticWorkVisibility and isset(self::$staticWorkVisibility[$profile]) ) { $this->_costVisibility=self::$staticCostVisibility[$profile]; $this->_workVisibility=self::$staticWorkVisibility[$profile]; return; } $user=getSessionUser(); $list=SqlList::getList('VisibilityScope', 'accessCode', null, false); $hCost=SqlElement::getSingleSqlElementFromCriteria('HabilitationOther', array('idProfile'=>$profile,'scope'=>'cost')); $hWork=SqlElement::getSingleSqlElementFromCriteria('HabilitationOther', array('idProfile'=>$profile,'scope'=>'work')); if ($hCost->id) { $this->_costVisibility=$list[$hCost->rightAccess]; } else { $this->_costVisibility='ALL'; } if ($hWork->id) { $this->_workVisibility=$list[$hWork->rightAccess]; } else { $this->_workVisibility='ALL'; } if (!self::$staticCostVisibility) self::$staticCostVisibility=array(); if (!self::$staticWorkVisibility) self::$staticWorkVisibility=array(); self::$staticCostVisibility[$profile]=$this->_costVisibility; self::$staticWorkVisibility[$profile]=$this->_workVisibility; } public function getFieldAttributes($fieldName) { if (! $this->_costVisibility or ! $this->_workVisibility) { $this->setVisibility(); } if ($this->_costVisibility =='NO') { if (substr($fieldName,-4)=='Cost' or substr($fieldName,0,7)=='expense' or substr($fieldName,0,5)=='total') { return 'hidden'; } } else if ($this->_costVisibility =='VAL') { if ( (substr($fieldName,-4)=='Cost' and $fieldName!='validatedCost') or (substr($fieldName,0,7)=='expense' and $fieldName!='expenseValidatedAmount') or (substr($fieldName,0,5)=='total' and $fieldName!='totalValidatedCost')) { return 'hidden'; } } if ($this->_workVisibility=='NO') { if (substr($fieldName,-4)=='Work') { return 'hidden'; } } else if ($this->_workVisibility=='VAL') { if ( substr($fieldName,-4)=='Work' and $fieldName!='validatedWork') { return 'hidden'; } } if ($this->id and $this->validatedCalculated) { if ($fieldName=='validatedWork' or $fieldName=='validatedCost') { return "readonly"; } } if ($this->id and $this->validatedExpenseCalculated) { if ($fieldName=='expenseValidatedAmount' and $this->$fieldName>0) { return "readonly"; } } return parent::getFieldAttributes($fieldName); } /** * Fulfill a planningElementList with : * - parents for each item * - predecessor for each item * @param List of PlanningElements */ public static function initializeFullList($list) { if (count($list)==0) return $list; $idList=array(); // $list must be sorted on WBS ! $result=$list; $listProjectsPriority=array(); // Parents foreach ($list as $id=>$pe) { if ($pe->refType=='Project') { $listProjectsPriority[$pe->refId]=$pe->priority; } $idList[$pe->id]=$pe->id; $pe->_parentList=array(); $pe->_childList=array(); if ($pe->topId) { if (array_key_exists('#'.$pe->topId, $result)) { $parent=$result['#'.$pe->topId]; } else { $parent=new PlanningElement($pe->topId); $parent->_parentList=array(); $parent->_predecessorList=array(); $parent->_predecessorListWithParent=array(); $parent->_noPlan=true; $parent->_childList=array(); $result['#'.$pe->topId]=$parent; } if (isset($parent->_parentList)) { $pe->_parentList=$parent->_parentList; } $pe->_parentList['#'.$pe->topId]=$pe->topId; } $result[$id]=$pe; } $reverse=array_reverse($result, true); foreach ($reverse as $id=>$pe) { if ($pe->topId) { if (array_key_exists('#'.$pe->topId, $result)) { $parent=$result['#'.$pe->topId]; } else { $parent=new PlanningElement($pe->topId); $parent->_parentList=array(); $parent->_predecessorList=array(); $parent->_predecessorListWithParent=array(); $parent->_noPlan=true; $parent->_childList=array(); $result['#'.$pe->topId]=$parent; } $parent=$result['#'.$pe->topId]; $parent->_childList=array_merge_preserve_keys($pe->_childList,$parent->_childList); $parent->_childList['#'.$pe->id]=$pe->id; $result['#'.$pe->topId]=$parent; } } // Predecessors $crit='successorId in (0,' . implode(',',$idList) . ')'; $dep=new Dependency(); $depList=$dep->getSqlElementsFromCriteria(null, false, $crit); $directPredecessors=array(); foreach ($depList as $dep) { if (! array_key_exists("#".$dep->successorId, $directPredecessors)) { $directPredecessors["#".$dep->successorId]=array(); } $lstPrec=$directPredecessors["#".$dep->successorId]; //$lstPrec["#".$dep->predecessorId]=$dep->predecessorId; $lstPrec["#".$dep->predecessorId]=$dep->dependencyDelay; // #77 : store delay of dependency if (! array_key_exists("#".$dep->predecessorId, $result)) { $parent=new PlanningElement($dep->predecessorId); $parent->_parentList=array(); $parent->_predecessorList=array(); $parent->_predecessorListWithParent=array(); $parent->_noPlan=true; $parent->_childList=array(); $result["#".$dep->predecessorId]=$parent; } $parentChilds=$result["#".$dep->predecessorId]->_childList; foreach ($parentChilds as $tmpIdChild=>$tempValChild) { $parentChilds[$tmpIdChild]=$dep->dependencyDelay; } if (isset($parentChilds["#".$dep->successorId])) { unset($parentChilds["#".$dep->successorId]); } // Self cannot be it own predecessor $directPredecessors["#".$dep->successorId]=array_merge_preserve_keys($lstPrec,$parentChilds); } foreach ($result as $id=>$pe) { $pe=$result[$id]; if (array_key_exists($id, $directPredecessors)) { $pe->_directPredecessorList=$directPredecessors[$id]; } else { $pe->_directPredecessorList=array(); } $visited=array(); $pe->_predecessorList=self::getRecursivePredecessor($directPredecessors,$id,$result,'main', $visited); $pe->_predecessorListWithParent=$pe->_predecessorList; foreach ($pe->_parentList as $idParent=>$parent) { $visited=array(); $pe->_predecessorListWithParent=array_merge($pe->_predecessorListWithParent,self::getRecursivePredecessor($directPredecessors,$idParent,$result,'parent', $visited)); } if (! $pe->realStartDate and ! (isset($pe->_noPlan) and $pe->_noPlan)) { $pe->plannedStartDate=null; } if (! $pe->realEndDate and ! (isset($pe->_noPlan) and $pe->_noPlan)) { $pe->plannedEndDate=null; } $result[$id]=$pe; } $result['_listProjectsPriority']=$listProjectsPriority; return $result; } private static function getRecursivePredecessor($directFullList, $id, $result,$scope,$visited) { if (isset($result[$id]->_predecessorList)) { return $result[$id]->_predecessorList; } if (array_key_exists($id, $directFullList)) { $result=$directFullList[$id]; foreach ($directFullList[$id] as $idPrec=>$prec) { if(array_key_exists($idPrec,$visited)) continue; $visited[$idPrec]=1; $result=array_merge($result,self::getRecursivePredecessor($directFullList,$idPrec,$result,$scope,$visited)); } } else { $result=array(); } return $result; } static function comparePlanningElementSimple($a, $b) { if ($a->_sortCriteria<$b->_sortCriteria) { return -1; } if ($a->_sortCriteria>$b->_sortCriteria) { return +1; } return 0; } static function copyStructure($obj, $newObj, $copyToOrigin=false, $copyToWithNotes=false, $copyToWithAttachments=false, $copyToWithLinks=false, $copyAssignments=false, $copyAffectations=false, $toProject=null, $copySubProjects=false) { self::$_noDispatch=true; // avoid recursive updates on each item, will be done only al elementary level $pe=new PlanningElement(); $list=$pe->getSqlElementsFromCriteria(array('topRefType'=>get_class($obj), 'topRefId'=>$obj->id),null,null,'wbsSortable asc'); foreach ($list as $pe) { // each planning element corresponding to item to copy if ($pe->refType!='Activity' and $pe->refType!='Project' and $pe->refType!='Milestone') continue; if ($pe->refType=='Project' and ! $copySubProjects) continue; $item=new $pe->refType($pe->refId); $type='id'.get_class($item).'Type'; $newItem=$item->copyTo(get_class($item),$item->$type, $item->name, $copyToOrigin, $copyToWithNotes, $copyToWithAttachments,$copyToWithLinks, $copyAssignments, $copyAffectations, $toProject, (get_class($newObj)=='Activity')?$newObj->id:null ); $resultItem=$newItem->_copyResult; unset($newItem->_copyResult); if (! stripos($resultItem,'id="lastOperationStatus" value="OK"')>0 ) { return $resultItem; } self::$_copiedItems[get_class($item).'#'.$item->id]=array('from'=>$item,'to'=>$newItem); if ($pe->refType=='Project' and $copyAffectations) { $aff=new Affectation(); $crit=array('idProject'=>$item->id); $lstAff=$aff->getSqlElementsFromCriteria($crit); foreach ($lstAff as $aff) { $critExists=array('idProject'=>$aff->idProject, 'idResource'=>$aff->idResource); $affExists=SqlElement::getSingleSqlElementFromCriteria('Affectation', $critExists); if (!$affExists or !$affExists->id) { $aff->id=null; $aff->idProject=$newItem->id; $aff->save(); } } } // recursively call copy structure $res=self::copyStructure($item, $newItem, $copyToOrigin, $copyToWithNotes, $copyToWithAttachments, $copyToWithLinks, $copyAssignments, $copyAffectations, ($pe->refType=='Project')?$newItem->id:$toProject,$copySubProjects); if ($res!='OK') { return $res; } } return "OK"; // No error ;) } static function copyStructureFinalize() { self::$_noDispatch=false; // Update synthesys for non elementary item (will just be done once ;) foreach (PlanningElement::$_noDispatchArray as $pe) { $method='updateSynthesis'.$pe->refType; if (method_exists($pe,$method )) { $res=$pe->$method(); } else { $res=$pe->updateSynthesisObj(); } } // copy dependencies $critWhere=""; foreach (self::$_copiedItems as $id=>$fromTo) { $from=$fromTo['from']; $critWhere.=(($critWhere)?',':'')."('".get_class($from)."','" . Sql::fmtId($from->id) . "')"; } if ($critWhere) { $clauseWhere="(predecessorRefType,predecessorRefId) in (" . $critWhere . ")" . " or (successorRefType,successorRefId) in (" . $critWhere . ")"; } else { $clauseWhere=" 1=0 "; } $dep=New dependency(); $deps=$dep->getSqlElementsFromCriteria(null, false, $clauseWhere); foreach ($deps as $dep) { if (array_key_exists($dep->predecessorRefType . "#" . $dep->predecessorRefId, self::$_copiedItems) ) { $to=self::$_copiedItems[$dep->predecessorRefType . "#" . $dep->predecessorRefId]['to']; $dep->predecessorRefType=get_class($to); $dep->predecessorRefId=$to->id; $crit=array('refType'=>get_class($to), 'refId'=>$to->id); $pe=SqlElement::getSingleSqlElementFromCriteria('PlanningElement', $crit); $dep->predecessorId=$pe->id; } if (array_key_exists($dep->successorRefType . "#" . $dep->successorRefId, self::$_copiedItems) ) { $to=self::$_copiedItems[$dep->successorRefType . "#" . $dep->successorRefId]['to']; $dep->successorRefType=get_class($to); $dep->successorRefId=$to->id; $crit=array('refType'=>get_class($to), 'refId'=>$to->id); $pe=SqlElement::getSingleSqlElementFromCriteria('PlanningElement', $crit); $dep->successorId=$pe->id; } $dep->id=null; $tmpRes=$dep->save(); if (! stripos($tmpRes,'id="lastOperationStatus" value="OK"')>0 ) { errorLog($tmpRes); // Will not raise an error but will trace it in log } } $result="OK"; } } ?>