mirror of
https://github.com/Dolibarr/dolibarr.git
synced 2025-12-06 17:48:25 +01:00
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:
@@ -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"> </option>';
|
if ($useempty) print '<option value="0"> </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--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user