diff --git a/htdocs/core/class/stats.class.php b/htdocs/core/class/stats.class.php index 1dc405ecbad..8e3c70c0829 100644 --- a/htdocs/core/class/stats.class.php +++ b/htdocs/core/class/stats.class.php @@ -388,6 +388,12 @@ abstract class Stats if($i>0 && $row->total) $result[$i-1]['total_diff'] = ($result[$i-1]['total'] - $row->total) / $row->total * 100; $result[$i]['avg'] = $row->avg; if($i>0 && $row->avg) $result[$i-1]['avg_diff'] = ($result[$i-1]['avg'] - $row->avg) / $row->avg * 100; + // For some $sql only + if (isset($row->weighted)) + { + $result[$i]['weighted'] = $row->weighted; + if($i>0 && $row->weighted) $result[$i-1]['avg_weighted'] = ($result[$i-1]['weighted'] - $row->weighted) / $row->weighted * 100; + } $i++; } $this->db->free($resql); diff --git a/htdocs/install/mysql/migration/3.9.0-4.0.0.sql b/htdocs/install/mysql/migration/3.9.0-4.0.0.sql index 0ddeaf8f3ad..8fc8ecb5df3 100644 --- a/htdocs/install/mysql/migration/3.9.0-4.0.0.sql +++ b/htdocs/install/mysql/migration/3.9.0-4.0.0.sql @@ -483,12 +483,12 @@ update llx_product_batch set batch = '000000' where batch = ''; update llx_product_lot set batch = '000000' where batch = 'Undefined'; update llx_stock_mouvement set batch = '000000' where batch = 'Undefined'; +ALTER TABLE llx_import_model MODIFY COLUMN type varchar(50); --- At end (higher risk of error) --- VMYSQL4.1 ALTER TABLE llx_c_type_resource CHANGE COLUMN rowid rowid integer NOT NULL AUTO_INCREMENT; +UPDATE llx_projet set fk_opp_status = NULL where fk_opp_status = -1; +UPDATE llx_c_lead_status set code = 'WON' where code = 'WIN'; -ALTER TABLE llx_product_batch ADD UNIQUE INDEX uk_product_batch (fk_product_stock, batch); CREATE TABLE llx_oauth_token ( rowid integer AUTO_INCREMENT PRIMARY KEY, @@ -508,4 +508,10 @@ CREATE TABLE llx_oauth_state ( entity integer )ENGINE=InnoDB; -ALTER TABLE llx_import_model MODIFY COLUMN type varchar(50); +-- At end (higher risk of error) + +-- VMYSQL4.1 ALTER TABLE llx_c_type_resource CHANGE COLUMN rowid rowid integer NOT NULL AUTO_INCREMENT; + +ALTER TABLE llx_product_batch ADD UNIQUE INDEX uk_product_batch (fk_product_stock, batch); + + diff --git a/htdocs/install/mysql/migration/repair.sql b/htdocs/install/mysql/migration/repair.sql index 8e445c1e1a6..147f516c479 100755 --- a/htdocs/install/mysql/migration/repair.sql +++ b/htdocs/install/mysql/migration/repair.sql @@ -202,6 +202,8 @@ UPDATE llx_actioncomm set fk_user_action = fk_user_author where fk_user_author > UPDATE llx_projet_task_time set task_datehour = task_date where task_datehour IS NULL and task_date IS NOT NULL; +UPDATE llx_projet set fk_opp_status = NULL where fk_opp_status = -1; +UPDATE llx_c_lead_status set code = 'WON' where code = 'WIN'; -- Requests to clean old tables or external modules tables diff --git a/htdocs/langs/en_US/projects.lang b/htdocs/langs/en_US/projects.lang index 1a370ecf47a..eb5e3f9402b 100644 --- a/htdocs/langs/en_US/projects.lang +++ b/htdocs/langs/en_US/projects.lang @@ -130,6 +130,9 @@ OpportunityProbability=Opportunity probability OpportunityProbabilityShort=Opp. probab. OpportunityAmount=Opportunity amount OpportunityAmountShort=Opp. amount +OpportunityAmountAverageShort=Average Opp. amount +OpportunityAmountWeigthedShort=Weighted Opp. amount +WonLostExcluded=Won/Lost excluded ##### Types de contacts ##### TypeContact_project_internal_PROJECTLEADER=Project leader TypeContact_project_external_PROJECTLEADER=Project leader diff --git a/htdocs/projet/class/project.class.php b/htdocs/projet/class/project.class.php index f29122f1c24..86548031d8b 100644 --- a/htdocs/projet/class/project.class.php +++ b/htdocs/projet/class/project.class.php @@ -248,7 +248,7 @@ class Project extends CommonObject $sql.= ", description = '" . $this->db->escape($this->description) . "'"; $sql.= ", fk_soc = " . ($this->socid > 0 ? $this->socid : "null"); $sql.= ", fk_statut = " . $this->statut; - $sql.= ", fk_opp_status = " . ((is_numeric($this->opp_status) && $this->opp_status != '') ? $this->opp_status : 'null'); + $sql.= ", fk_opp_status = " . ((is_numeric($this->opp_status) && $this->opp_status > 0) ? $this->opp_status : 'null'); $sql.= ", opp_percent = " . ((is_numeric($this->opp_percent) && $this->opp_percent != '') ? $this->opp_percent : 'null'); $sql.= ", public = " . ($this->public ? 1 : 0); $sql.= ", datec=" . ($this->date_c != '' ? "'".$this->db->idate($this->date_c)."'" : 'null'); diff --git a/htdocs/projet/class/projectstats.class.php b/htdocs/projet/class/projectstats.class.php index 4bb6a1f31fa..88eb8ef5ced 100644 --- a/htdocs/projet/class/projectstats.class.php +++ b/htdocs/projet/class/projectstats.class.php @@ -41,7 +41,8 @@ class ProjectStats extends Stats /** - * Return all leads grouped by status + * Return all leads grouped by status. + * Warning: There is no filter on WON/LOST because we want this for statistics. * * @param int $limit Limit results * @return array|int Array with value or -1 if error @@ -58,7 +59,7 @@ class ProjectStats extends Stats $sql .= " FROM " . MAIN_DB_PREFIX . "projet as t, ".MAIN_DB_PREFIX."c_lead_status as cls"; $sql .= $this->buildWhere(); $sql .= " AND t.fk_opp_status = cls.rowid"; - $sql .= " AND t.fk_statut <> 0"; // We want historic also, so all projects + $sql .= " AND t.fk_statut <> 0"; // We want historic also, so all projects not draft $sql .= " GROUP BY t.fk_opp_status, cls.code, cls.label"; $result = array (); @@ -110,13 +111,16 @@ class ProjectStats extends Stats $datay = array (); - $sql = "SELECT date_format(t.datec,'%Y') as year, COUNT(t.rowid) as nb, SUM(t.opp_amount) as total, AVG(t.opp_amount) as avg"; - $sql .= " FROM " . MAIN_DB_PREFIX . "projet as t"; + $wonlostfilter=0; // No filter on status WON/LOST + + $sql = "SELECT date_format(t.datec,'%Y') as year, COUNT(t.rowid) as nb, SUM(t.opp_amount) as total, AVG(t.opp_amount) as avg,"; + $sql.= " SUM(t.opp_amount * ".$this->db->ifsql("t.opp_percent IS NULL".($wonlostfilter?" OR cls.code IN ('WON','LOST')":""), '0', 't.opp_percent')." / 100) as weighted"; + $sql.= " FROM " . MAIN_DB_PREFIX . "projet as t LEFT JOIN ".MAIN_DB_PREFIX."c_lead_status as cls ON cls.rowid = t.fk_opp_status"; if (! $user->rights->societe->client->voir && ! $user->societe_id) $sql .= " INNER JOIN " . MAIN_DB_PREFIX . "societe_commerciaux as sc ON sc.fk_soc=t.fk_soc AND sc.fk_user=" . $user->id; - $sql .= $this->buildWhere(); - $sql .= " GROUP BY year"; - $sql .= $this->db->order('year', 'DESC'); + $sql.= $this->buildWhere(); + $sql.= " GROUP BY year"; + $sql.= $this->db->order('year', 'DESC'); return $this->_getAllByYear($sql); } @@ -213,9 +217,10 @@ class ProjectStats extends Stats * @param int $endyear Start year * @param int $startyear End year * @param int $cachedelay Delay we accept for cache file (0=No read, no save of cache, -1=No read but save) + * @param int $wonlostfilter Add a filter on status won/lost * @return array Array of values */ - function getWeightedAmountByMonthWithPrevYear($endyear,$startyear,$cachedelay=0) + function getWeightedAmountByMonthWithPrevYear($endyear,$startyear,$cachedelay=0,$wonlostfilter=1) { global $conf,$user,$langs; @@ -262,7 +267,7 @@ class ProjectStats extends Stats $year=$startyear; while($year <= $endyear) { - $datay[$year] = $this->getWeightedAmountByMonth($year); + $datay[$year] = $this->getWeightedAmountByMonth($year,$wonlostfilter); $year++; } @@ -302,18 +307,19 @@ class ProjectStats extends Stats /** - * Return the Project weighted opp amount by month for a year + * Return the Project weighted opp amount by month for a year. * - * @param int $year scan - * @return array with amount by month + * @param int $year Year to scan + * @param int $wonlostfilter Add a filter on status won/lost + * @return array Array with amount by month */ - function getWeightedAmountByMonth($year) + function getWeightedAmountByMonth($year, $wonlostfilter=1) { global $user; $this->yearmonth = $year; - $sql = "SELECT date_format(t.datec,'%m') as dm, SUM(t.opp_amount * ".$this->db->ifsql('cls.percent IS NULL', '0', 'cls.percent')." / 100)"; + $sql = "SELECT date_format(t.datec,'%m') as dm, SUM(t.opp_amount * ".$this->db->ifsql("t.opp_percent IS NULL".($wonlostfilter?" OR cls.code IN ('WON','LOST')":""), '0', 't.opp_percent')." / 100)"; $sql .= " FROM " . MAIN_DB_PREFIX . "projet as t LEFT JOIN ".MAIN_DB_PREFIX.'c_lead_status as cls ON t.fk_opp_status = cls.rowid'; if (! $user->rights->societe->client->voir && ! $user->societe_id) $sql .= " INNER JOIN " . MAIN_DB_PREFIX . "societe_commerciaux as sc ON sc.fk_soc=t.fk_soc AND sc.fk_user=" . $user->id; diff --git a/htdocs/projet/graph_opportunities.inc.php b/htdocs/projet/graph_opportunities.inc.php index 199840bb1c6..673fbc973ad 100644 --- a/htdocs/projet/graph_opportunities.inc.php +++ b/htdocs/projet/graph_opportunities.inc.php @@ -1,13 +1,14 @@ global->PROJECT_USE_OPPORTUNITIES)) { - $sql = "SELECT COUNT(p.rowid) as nb, SUM(p.opp_amount) as opp_amount, SUM(p.opp_amount * p.opp_percent) as ponderated_opp_amount, p.fk_opp_status as opp_status"; - $sql.= " FROM ".MAIN_DB_PREFIX."projet as p"; + $sql = "SELECT p.fk_opp_status as opp_status, cls.code, COUNT(p.rowid) as nb, SUM(p.opp_amount) as opp_amount, SUM(p.opp_amount * p.opp_percent) as ponderated_opp_amount"; + $sql.= " FROM ".MAIN_DB_PREFIX."projet as p, ".MAIN_DB_PREFIX."c_lead_status as cls"; $sql.= " WHERE p.entity = ".$conf->entity; - $sql.= " AND p.fk_statut = 1"; + $sql.= " AND p.fk_opp_status = cls.rowid"; + $sql.= " AND p.fk_statut = 1"; // Opend projects only if ($mine || empty($user->rights->projet->all->lire)) $sql.= " AND p.rowid IN (".$projectsListId.")"; if ($socid) $sql.= " AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = ".$socid.")"; - $sql.= " GROUP BY p.fk_opp_status"; + $sql.= " GROUP BY p.fk_opp_status, cls.code"; $resql = $db->query($sql); if ($resql) @@ -34,8 +35,11 @@ if (! empty($conf->global->PROJECT_USE_OPPORTUNITIES)) $valsamount[$obj->opp_status]=$obj->opp_amount; $totalnb+=$obj->nb; if ($obj->opp_status) $totaloppnb+=$obj->nb; - $totalamount+=$obj->opp_amount; - $ponderated_opp_amount+=$obj->ponderated_opp_amount; + if (! in_array($obj->code, array('WON', 'LOST'))) + { + $totalamount+=$obj->opp_amount; + $ponderated_opp_amount+=$obj->ponderated_opp_amount; + } } $total+=$row[0]; } @@ -79,8 +83,11 @@ if (! empty($conf->global->PROJECT_USE_OPPORTUNITIES)) } //if ($totalinprocess != $total) //print '