Fix: [ bug #1354 ] Tasks disapear in same sub-task

Conflicts:
	htdocs/install/mysql/migration/repair.sql
	htdocs/projet/class/project.class.php
This commit is contained in:
Laurent Destailleur
2014-04-26 21:47:15 +02:00
parent 4ecfa1d6e0
commit 537ada96a8
6 changed files with 166 additions and 80 deletions

View File

@@ -431,9 +431,10 @@ class FormOther
* @param int $modetask 1 to restrict on tasks associated to user * @param int $modetask 1 to restrict on tasks associated to user
* @param int $mode 0=Return list of tasks and their projects, 1=Return projects and tasks if exists * @param int $mode 0=Return list of tasks and their projects, 1=Return projects and tasks if exists
* @param int $useempty 0=Allow empty values * @param int $useempty 0=Allow empty values
* @param int $disablechildoftaskid 1=Disable task that are child of the provided task id
* @return void * @return void
*/ */
function selectProjectTasks($selectedtask='', $projectid=0, $htmlname='task_parent', $modeproject=0, $modetask=0, $mode=0, $useempty=0) function selectProjectTasks($selectedtask='', $projectid=0, $htmlname='task_parent', $modeproject=0, $modetask=0, $mode=0, $useempty=0, $disablechildoftaskid=0)
{ {
global $user, $langs; global $user, $langs;
@@ -448,7 +449,7 @@ class FormOther
if ($useempty) print '<option value="0">&nbsp;</option>'; if ($useempty) print '<option value="0">&nbsp;</option>';
$j=0; $j=0;
$level=0; $level=0;
$this->_pLineSelect($j, 0, $tasksarray, $level, $selectedtask, $projectid); $this->_pLineSelect($j, 0, $tasksarray, $level, $selectedtask, $projectid, $disablechildoftaskid);
print '</select>'; print '</select>';
} }
else else
@@ -458,17 +459,18 @@ class FormOther
} }
/** /**
* Write all lines of a project (if parent = 0) * Write lines of a project (all lines of a project if parent = 0)
* *
* @param int &$inc Cursor counter * @param int &$inc Cursor counter
* @param int $parent Id parent * @param int $parent Id of parent task we want to see
* @param Object $lines Line object * @param array $lines Array of task lines
* @param int $level Level * @param int $level Level
* @param int $selectedtask Id selected task * @param int $selectedtask Id selected task
* @param int $selectedproject Id selected project * @param int $selectedproject Id selected project
* @param int $disablechildoftaskid 1=Disable task that are child of the provided task id
* @return void * @return void
*/ */
private function _pLineSelect(&$inc, $parent, $lines, $level=0, $selectedtask=0, $selectedproject=0) private function _pLineSelect(&$inc, $parent, $lines, $level=0, $selectedtask=0, $selectedproject=0, $disablechildoftaskid=0)
{ {
global $langs, $user, $conf; global $langs, $user, $conf;
@@ -481,12 +483,12 @@ class FormOther
{ {
$var = !$var; $var = !$var;
//var_dump($selectedtask."--".$selectedtask."--".$lines[$i]->fk_project."_".$lines[$i]->id); //var_dump($selectedproject."--".$selectedtask."--".$lines[$i]->fk_project."_".$lines[$i]->id);
// Break on a new project // Break on a new project
if ($parent == 0) if ($parent == 0) // We are on a task at first level
{ {
if ($lines[$i]->fk_project != $lastprojectid) if ($lines[$i]->fk_project != $lastprojectid) // Break found on project
{ {
if ($i > 0 && $conf->browser->firefox) print '<option value="0" disabled="disabled">----------</option>'; if ($i > 0 && $conf->browser->firefox) print '<option value="0" disabled="disabled">----------</option>';
print '<option value="'.$lines[$i]->fk_project.'_0"'; print '<option value="'.$lines[$i]->fk_project.'_0"';
@@ -509,11 +511,22 @@ class FormOther
} }
} }
$newdisablechildoftaskid=$disablechildoftaskid;
// Print task // Print task
if ($lines[$i]->id >= 0) if ($lines[$i]->id >= 0)
{ {
// Check if we must disable entry
$disabled=0;
if ($disablechildoftaskid && (($lines[$i]->id == $disablechildoftaskid || $lines[$i]->fk_parent == $disablechildoftaskid)))
{
$disabled++;
if ($lines[$i]->fk_parent == $disablechildoftaskid) $newdisablechildoftaskid=$lines[$i]->id; // If task is child of a disabled parent, we will propagate id to disable next child too
}
print '<option value="'.$lines[$i]->fk_project.'_'.$lines[$i]->id.'"'; print '<option value="'.$lines[$i]->fk_project.'_'.$lines[$i]->id.'"';
if (($lines[$i]->id == $selectedtask) || ($lines[$i]->fk_project.'_'.$lines[$i]->id == $selectedtask)) print ' selected="selected"'; if (($lines[$i]->id == $selectedtask) || ($lines[$i]->fk_project.'_'.$lines[$i]->id == $selectedtask)) print ' selected="selected"';
if ($disabled) print ' disabled="disabled"';
print '>'; print '>';
print $langs->trans("Project").' '.$lines[$i]->projectref; print $langs->trans("Project").' '.$lines[$i]->projectref;
if (empty($lines[$i]->public)) if (empty($lines[$i]->public))
@@ -534,7 +547,7 @@ class FormOther
} }
$level++; $level++;
if ($lines[$i]->id) $this->_pLineSelect($inc, $lines[$i]->id, $lines, $level, $selectedtask, $selectedproject); if ($lines[$i]->id) $this->_pLineSelect($inc, $lines[$i]->id, $lines, $level, $selectedtask, $selectedproject, $newdisablechildoftaskid);
$level--; $level--;
} }
} }

View File

@@ -1532,3 +1532,125 @@ function dolGetElementUrl($objectid,$objecttype,$withpicto=0,$option='')
} }
return $ret; return $ret;
} }
/**
* Clean corrupted tree (orphelins linked to a not existing parent), record linked to themself and child-parent loop
*
* @param string $tabletocleantree Table to clean
* @param string $fieldfkparent Field name that contains id of parent
* @return int Nb of records fixed/deleted
*/
function cleanCorruptedTree($db, $tabletocleantree, $fieldfkparent)
{
$totalnb=0;
$listofid=array();
$listofparentid=array();
// Get list of all id in array listofid and all parents in array listofparentid
$sql='SELECT rowid, '.$fieldfkparent.' as parent_id FROM '.MAIN_DB_PREFIX.$tabletocleantree;
$resql = $db->query($sql);
if ($resql)
{
$num = $db->num_rows($resql);
$i = 0;
while ($i < $num)
{
$obj = $db->fetch_object($resql);
$listofid[]=$obj->rowid;
if ($obj->parent_id > 0) $listofparentid[$obj->rowid]=$obj->parent_id;
$i++;
}
}
else
{
dol_print_error($db);
}
if (count($listofid))
{
print 'Code requested to clean tree (may be to solve data corruption), so we check/clean orphelins and loops.'."<br>\n";
// Check loops on each other
$sql = "UPDATE ".MAIN_DB_PREFIX.$tabletocleantree." SET ".$fieldfkparent." = 0 WHERE ".$fieldfkparent." = rowid"; // So we update only records linked to themself
dol_syslog("sql=".$sql);
$resql = $db->query($sql);
if ($resql)
{
$nb=$db->affected_rows($sql);
if ($nb > 0)
{
print '<br>Some record that were parent of themself were cleaned.';
}
$totalnb+=$nb;
}
//else dol_print_error($db);
// Check other loops
$listofidtoclean=array();
foreach($listofparentid as $id => $pid)
{
// Check depth
//print 'Analyse record id='.$id.' with parent '.$pid.'<br>';
$cursor=$id; $arrayidparsed=array(); // We start from child $id
while ($cursor > 0)
{
$arrayidparsed[$cursor]=1;
if ($arrayidparsed[$listofparentid[$cursor]]) // We detect a loop. A record with a parent that was already into child
{
print 'Found a loop between id '.$id.' - '.$cursor.'<br>';
unset($arrayidparsed);
$listofidtoclean[$cursor]=$id;
break;
}
$cursor=$listofparentid[$cursor];
}
if (count($listofidtoclean)) break;
}
$sql = "UPDATE ".MAIN_DB_PREFIX.$tabletocleantree;
$sql.= " SET ".$fieldfkparent." = 0";
$sql.= " WHERE rowid IN (".join(',',$listofidtoclean).")"; // So we update only records detected wrong
dol_syslog("sql=".$sql);
$resql = $db->query($sql);
if ($resql)
{
$nb=$db->affected_rows($sql);
if ($nb > 0)
{
// Removed orphelins records
print '<br>Some records were detected to have parent that is a child, we set them as root record for id: ';
print join(',',$listofidtoclean);
}
$totalnb+=$nb;
}
//else dol_print_error($db);
// Check and clean orphelins
$sql = "UPDATE ".MAIN_DB_PREFIX.$tabletocleantree;
$sql.= " SET ".$fieldfkparent." = 0";
$sql.= " WHERE ".$fieldfkparent." NOT IN (".join(',',$listofid).")"; // So we update only records linked to a non existing parent
dol_syslog("sql=".$sql);
$resql = $db->query($sql);
if ($resql)
{
$nb=$db->affected_rows($sql);
if ($nb > 0)
{
// Removed orphelins records
print '<br>Some orphelins were found and modified to be parent so records are visible again for id: ';
print join(',',$listofid);
}
$totalnb+=$nb;
}
//else dol_print_error($db);
print '<br>We fixed '.$totalnb.' record(s). Some records may still be corrupted. New check may be required.';
return $totalnb;
}
}

View File

@@ -111,3 +111,6 @@ UPDATE llx_product p SET p.stock= (SELECT SUM(ps.reel) FROM llx_product_stock ps
-- DROP TABLE llx_product_fournisseur; -- DROP TABLE llx_product_fournisseur;
-- ALTER TABLE llx_product_fournisseur_price DROP COLUMN fk_product_fournisseur; -- ALTER TABLE llx_product_fournisseur_price DROP COLUMN fk_product_fournisseur;
ALTER TABLE llx_product_fournisseur_price DROP FOREIGN KEY fk_product_fournisseur; ALTER TABLE llx_product_fournisseur_price DROP FOREIGN KEY fk_product_fournisseur;
UPDATE llx_projet_task SET fk_task_parent = 0 WHERE fk_task_parent = rowid

View File

@@ -1280,66 +1280,6 @@ class Project extends CommonObject
return $result; return $result;
} }
/**
* Clean tasks not linked to an existing parent
*
* @return int Nb of records deleted
*/
function clean_orphelins()
{
$nb=0;
// There is orphelins. We clean that
$listofid=array();
// Get list of all id in array listofid
$sql='SELECT rowid FROM '.MAIN_DB_PREFIX.'projet_task';
$resql = $this->db->query($sql);
if ($resql)
{
$num = $this->db->num_rows($resql);
$i = 0;
while ($i < $num && $i < 100)
{
$obj = $this->db->fetch_object($resql);
$listofid[]=$obj->rowid;
$i++;
}
}
else
{
dol_print_error($this->db);
}
if (count($listofid))
{
print 'Code asked to check and clean orphelins.';
$sql = "UPDATE ".MAIN_DB_PREFIX."projet_task";
$sql.= " SET fk_task_parent = 0";
$sql.= " WHERE fk_task_parent NOT IN (".join(',',$listofid).")"; // So we update only records linked to a non existing parent
$resql = $this->db->query($sql);
if ($resql)
{
$nb=$this->db->affected_rows($sql);
if ($nb > 0)
{
// Removed orphelins records
print 'Some orphelins were found and modified to be parent so records are visible again: ';
print join(',',$listofid);
}
return $nb;
}
else
{
return -1;
}
}
}
/** /**
* Associate element to a project * Associate element to a project

View File

@@ -438,11 +438,19 @@ else
{ {
if ($mode=='mine') if ($mode=='mine')
{ {
if ($nboftaskshown < count($tasksrole)) $object->clean_orphelins(); if ($nboftaskshown < count($tasksrole))
{
include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
cleanCorruptedTree($db, 'projet_task', 'fk_task_parent');
}
} }
else else
{ {
if ($nboftaskshown < count($tasksarray)) $object->clean_orphelins(); if ($nboftaskshown < count($tasksarray))
{
include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
cleanCorruptedTree($db, 'projet_task', 'fk_task_parent');
}
} }
} }
} }

View File

@@ -318,7 +318,7 @@ if ($id > 0 || ! empty($ref))
// Task parent // Task parent
print '<tr><td>'.$langs->trans("ChildOfTask").'</td><td>'; print '<tr><td>'.$langs->trans("ChildOfTask").'</td><td>';
print $formother->selectProjectTasks($object->fk_task_parent,$projectstatic->id, 'task_parent', $user->admin?0:1, 0); print $formother->selectProjectTasks($object->fk_task_parent, $projectstatic->id, 'task_parent', ($user->admin?0:1), 0, 0, 0, $object->id);
print '</td></tr>'; print '</td></tr>';
// Date start // Date start