* SPDX-License-Identifier: AGPL-3.0-only
************************************/
/* crmv@44323 crmv@53923 */
class BudgetReportRun extends ReportRun {
// TODO: abilita export
public $enableExportPdf = false;
public $enableExportXls = false;
public $enablePrint = false;
public $hideParamsBlock = true;
// private stuff
protected $budgetPeriod = 'year';
protected $budgetSubperiod = null; // changed in constructor
protected $budgetPerUser = false;
function __construct($reportid) {
parent::__construct($reportid);
$this->reportid = $reportid;
$this->primarymodule = 'Potentials';
$this->secondarymodule = '';
$this->reporttype = '';
//$this->reportname = Nuovo Report SDK;
$this->columns = array();
if (!empty($_REQUEST['budgetPeriod'])) {
$this->budgetPeriod = $_REQUEST['budgetPeriod'];
}
if (!empty($_REQUEST['budgetSubperiod'])) {
$this->budgetSubperiod = $_REQUEST['budgetSubperiod'];
}
if (empty($this->budgetSubperiod)) {
// set a default
switch ($this->budgetPeriod) {
case 'year':
$this->budgetSubperiod = 'year_'.date('Y');
break;
case 'months6':
$this->budgetSubperiod = 'months6_1';
break;
case 'months4':
$this->budgetSubperiod = 'months4_1';
break;
case 'months3':
$this->budgetSubperiod = 'months3_1';
break;
case 'month':
$this->budgetSubperiod = 'month_'.date('m');
break;
}
}
if (!empty($_REQUEST['budgetPerUser'])) {
$this->budgetPerUser = in_array($_REQUEST['budgetPerUser'], array('on', 'true', '1'));
}
}
function getPrimaryStdFilterHTML() {
return '';
}
function getSecondaryStdFilterHTML() {
return '';
}
function getDatesPicklist() {
$html = "";
return $html;
}
function getSDKBlock() {
// TODO: ci sono url multipli //crmv@59091
$html = getTranslatedString('LBL_PERIOD', 'APP_STRINGS').":
";
$html .= $this->getDatesPicklist();
$html .= 'budgetPerUser ? 'checked="checked"' : '').'>';
return $html;
}
function getQueryDateCondition($column = '') {
global $table_prefix;
$limit1 = $limit2 = '';
switch ($this->budgetPeriod) {
case 'year': {
$limit1 = str_replace('year_', '', $this->budgetSubperiod).'-01-01';
$limit2 = str_replace('year_', '', $this->budgetSubperiod).'-12-31';
break;
}
case 'months6': {
if ($this->budgetSubperiod == 'months6_1') {
$limit1 = date('Y').'-01-01';
$limit2 = date('Y').'-06-30';
} else {
$limit1 = date('Y').'-07-01';
$limit2 = date('Y').'-12-31';
}
break;
}
case 'months4': {
if ($this->budgetSubperiod == 'months4_1') {
$limit1 = date('Y').'-01-01';
$limit2 = date('Y').'-04-30';
} elseif ($this->budgetSubperiod == 'months4_2') {
$limit1 = date('Y').'-05-01';
$limit2 = date('Y').'-08-31';
} else {
$limit1 = date('Y').'-09-01';
$limit2 = date('Y').'-12-31';
}
break;
}
case 'months3': {
if ($this->budgetSubperiod == 'months3_1') {
$limit1 = date('Y').'-01-01';
$limit2 = date('Y').'-03-31';
} elseif ($this->budgetSubperiod == 'months3_2') {
$limit1 = date('Y').'-04-01';
$limit2 = date('Y').'-06-30';
} elseif ($this->budgetSubperiod == 'months3_3') {
$limit1 = date('Y').'-07-01';
$limit2 = date('Y').'-09-30';
} else {
$limit1 = date('Y').'-10-01';
$limit2 = date('Y').'-12-31';
}
break;
}
case 'month': {
$m = str_replace('month_', '', $this->budgetSubperiod);
$dm = cal_days_in_month(CAL_GREGORIAN, $m, date('Y'));
$sm = str_pad($m, 2, '0', STR_PAD_LEFT);
$limit1 = date('Y')."-$sm-01";
$limit2 = date('Y')."-$sm-$dm";
break;
}
}
if (!empty($limit1) && !empty($limit2)) {
if (empty($column)) $column = "{$table_prefix}_potential.closingdate";
$cond = "AND $column BETWEEN '$limit1' AND '$limit2'";
}
return $cond;
}
function getData($reportid,$filterlist,$outputformat) {
global $adb, $table_prefix, $current_user;
$potFocus = CRMEntity::getInstance('Potentials');
$potTab = $potFocus->table_name;
$whereCond = $this->getQueryDateCondition();
//$whereCloseCond = preg_replace('/^\s*and\s*/i', '', $whereCond);
//$whereCloseCond = str_replace('pot.closingdate', 'pot.eff_closingdate', $whereCloseCond);
// crmv@49622
if ($this->budgetPerUser) {
//$whereCloseCond = str_replace('pot.', 'ipot.', $whereCloseCond);
$query =
"select
{$table_prefix}_crmentity.smownerid,
sum(case when $potTab.probability >= 70 then $potTab.amount else 0 end) as best,
sum(case when $potTab.probability >= 80 then $potTab.amount else 0 end) as forecast,
sum(case when $potTab.probability >= 90 then $potTab.amount else 0 end) as worst
from {$table_prefix}_potential
inner join {$table_prefix}_crmentity on {$table_prefix}_crmentity.crmid = $potTab.potentialid
POTJOIN
".$potFocus->getNonAdminAccessControlQuery('Potentials',$current_user)."
where
{$table_prefix}_crmentity.deleted = 0
$whereCond
group by {$table_prefix}_crmentity.smownerid";
$query = $potFocus->listQueryNonAdminChange($query, 'Potentials');
$query = str_replace('POTJOIN',"
inner join (
select distinct pq.potentialid
from {$table_prefix}_quotes pq
inner join {$table_prefix}_crmentity pqcrm on pqcrm.crmid = pq.quoteid
where pqcrm.deleted = 0 and pq.potentialid > 0
) quotes on quotes.potentialid = $potTab.potentialid", $query
);
} else {
$query =
"select
pl.productlineid,
0 as best,
0 as forecast,
0 as worst
from {$table_prefix}_potential
inner join {$table_prefix}_crmentity on {$table_prefix}_crmentity.crmid = $potTab.potentialid
inner join {$table_prefix}_quotes q on q.potentialid = $potTab.potentialid and q.quotestage in ('Created', 'Delivered')
inner join {$table_prefix}_crmentity qcrm on qcrm.crmid = q.quoteid
LINEIDJOIN
".$potFocus->getNonAdminAccessControlQuery('Potentials',$current_user)."
where
{$table_prefix}_crmentity.deleted = 0 and qcrm.deleted = 0
$whereCond
group by pl.productlineid";
// add security parameters
$query = $potFocus->listQueryNonAdminChange($query, 'Potentials');
// replace the join with linesid (troubles in the listQueryChange, so do it later)
$query = str_replace('LINEIDJOIN',"
inner join (
select distinct ipr.id, pl.productlineid
from {$table_prefix}_inventoryproductrel ipr
inner join {$table_prefix}_products p on p.productid = ipr.productid
inner join {$table_prefix}_crmentity pcrm on pcrm.crmid = p.productid
left join {$table_prefix}_productlines pl on pl.productlineid = p.productlineid
left join {$table_prefix}_crmentity plcrm on plcrm.crmid = pl.productlineid
where pcrm.deleted = 0 and (plcrm.deleted is null or plcrm.deleted = 0)
) pl on pl.id = q.quoteid",
$query);
}
// crmv@49622e
// retrieve potentials
$respot = $adb->pquery($query, array());
$data = array();
while ($row = $adb->FetchByAssoc($respot, -1, false)) {
// calculate the order amount for product line
if ($this->budgetPerUser) {
$orderAmount = $this->getOrderAmountByUser($row['smownerid'], $potFocus);
} else {
$orderAmount = $this->getOrderAmountByProdLine($row['productlineid'], $potFocus);
$forecast = $this->getForecastsByProdLine($row['productlineid'], $potFocus);
$row['best'] = $forecast['best'];
$row['forecast'] = $forecast['forecast'];
$row['worst'] = $forecast['worst'];
}
$row['orders_tot'] = $orderAmount;
$data[] = $row;
}
return $data;
}
function getForecastsByProdLine($lineid, &$potFocus) {
$values = array('best'=>0, 'forecast' => 0, 'worst' => 0);
$lineid = intval($lineid);
$potentials = $this->getPotentialsByProdLine($lineid, $potFocus);
foreach ($potentials as $potinfo) {
if ($potinfo['probability'] >= 70) {
$quoteid = $potinfo['quoteid'];
$lineinfo = $potFocus->getProdLinesInfo($quoteid, 'Quotes');
if ($lineinfo && is_array($lineinfo['list'])) {
foreach ($lineinfo['list'] as $line) {
if ($line['productlineid'] == $lineid) {
if ($potinfo['probability'] >= 70) {
$values['best'] += $line['total'];
if ($potinfo['probability'] >= 80) {
$values['forecast'] += $line['total'];
if ($potinfo['probability'] >= 90) {
$values['worst'] += $line['total'];
}
}
}
break;
}
}
}
}
}
return $values;
}
function getOrderAmountByUser($userid, &$potFocus) {
global $adb, $table_prefix, $current_user;
$userid = intval($userid);
// first get the relevant orders
$queryGenerator = QueryGenerator::getInstance('SalesOrder', $current_user);
$queryGenerator->addField('subject');
$where = $this->getQueryDateCondition("{$table_prefix}_salesorder.duedate");
$queryGenerator->appendToWhereClause(" $where AND {$table_prefix}_crmentity.deleted = 0 AND {$table_prefix}_salesorder.sostatus NOT IN ('Cancelled')");
$queryGenerator->appendToWhereClause(" AND {$table_prefix}_crmentity.smownerid = '$userid'");
$query = $queryGenerator->getQuery();
$query = replaceSelectQuery($query, "{$table_prefix}_crmentity.crmid");
$soids = array();
$res = $adb->query($query);
while ($row = $adb->fetchByAssoc($res, -1, false)) {
$soids[] = $row['crmid'];
}
//now I have all the required sales order id
$total = 0;
foreach ($soids as $sid) {
$lineinfo = $potFocus->getProdLinesInfo($sid, 'SalesOrder');
if ($lineinfo && is_array($lineinfo['list'])) {
$total += $lineinfo['linestotal'];
}
}
return $total;
}
function getOrderAmountByProdLine($lineid, &$potFocus) {
global $adb, $table_prefix, $current_user;
$lineid = intval($lineid);
// first get the relevant orders
$queryGenerator = QueryGenerator::getInstance('SalesOrder', $current_user);
$queryGenerator->addField('subject');
$queryGenerator->appendToFromClause("
INNER JOIN {$table_prefix}_inventoryproductrel ipr on ipr.id = {$table_prefix}_salesorder.salesorderid
inner join {$table_prefix}_products p on p.productid = ipr.productid
inner join {$table_prefix}_crmentity pcrm on pcrm.crmid = p.productid
left join {$table_prefix}_productlines pl on pl.productlineid = p.productlineid
left join {$table_prefix}_crmentity plcrm on plcrm.crmid = pl.productlineid
");
$where = $this->getQueryDateCondition("{$table_prefix}_salesorder.duedate");
$queryGenerator->appendToWhereClause(" $where AND pcrm.deleted = 0 AND {$table_prefix}_salesorder.sostatus NOT IN ('Cancelled')");
if ($lineid > 0) {
$queryGenerator->appendToWhereClause(" AND pl.productlineid = '$lineid' AND plcrm.deleted = 0");
} else {
$queryGenerator->appendToWhereClause(" AND pl.productlineid IS NULL");
}
$query = $queryGenerator->getQuery();
$query = replaceSelectQuery($query, "distinct {$table_prefix}_crmentity.crmid");
$soids = array();
$res = $adb->query($query);
while ($row = $adb->fetchByAssoc($res, -1, false)) {
$soids[] = $row['crmid'];
}
//now I have all the required sales order id
$total = 0;
foreach ($soids as $sid) {
$lineinfo = $potFocus->getProdLinesInfo($sid, 'SalesOrder');
if ($lineinfo && is_array($lineinfo['list'])) {
foreach ($lineinfo['list'] as $line) {
if ($line['productlineid'] == $lineid) {
$total += $line['total'];
break;
}
}
}
}
return $total;
}
function getPotentialsByUser($userid, &$potFocus) {
global $adb, $table_prefix, $current_user;
$userid = intval($userid);
$potTab = $table_prefix.'_potential';
$data = array();
$whereCond = $this->getQueryDateCondition();
$whereCond .= " AND {$table_prefix}_crmentity.smownerid = '$userid'";
$query =
"select
{$table_prefix}_crmentity.smownerid, $potTab.potentialid, $potTab.potentialname, $potTab.probability, q.quoteid
from {$table_prefix}_potential
inner join {$table_prefix}_crmentity on {$table_prefix}_crmentity.crmid = $potTab.potentialid
inner join {$table_prefix}_quotes q on q.potentialid = $potTab.potentialid and q.quotestage in ('Created', 'Delivered')
inner join {$table_prefix}_crmentity qcrm on qcrm.crmid = q.quoteid
POTJOIN
".$potFocus->getNonAdminAccessControlQuery('Potentials',$current_user)."
where
{$table_prefix}_crmentity.deleted = 0
$whereCond";
$query = $potFocus->listQueryNonAdminChange($query, 'Potentials');
$query = str_replace('POTJOIN',"
inner join (
select distinct pq.potentialid
from {$table_prefix}_quotes pq
inner join {$table_prefix}_crmentity pqcrm on pqcrm.crmid = pq.quoteid
where pqcrm.deleted = 0 and pq.potentialid > 0
) quotes on quotes.potentialid = $potTab.potentialid", $query
);
$respot = $adb->query($query);
$data = array();
while ($row = $adb->FetchByAssoc($respot, -1, false)) {
$data[] = $row;
}
return $data;
}
function getPotentialsByProdLine($prodlineid, &$potFocus) {
global $adb, $table_prefix, $current_user;
$prodlineid = intval($prodlineid);
$potTab = $table_prefix.'_potential';
$data = array();
$whereCond = $this->getQueryDateCondition();
if ($prodlineid > 0) {
$whereCond .= " AND pl.productlineid = '$prodlineid'";
} else {
$whereCond .= " AND pl.productlineid IS NULL";
}
$query =
"select
$potTab.potentialid, $potTab.potentialname, $potTab.probability, q.quoteid
from {$table_prefix}_potential
inner join {$table_prefix}_crmentity on {$table_prefix}_crmentity.crmid = $potTab.potentialid
inner join {$table_prefix}_quotes q on q.potentialid = $potTab.potentialid and q.quotestage in ('Created', 'Delivered')
inner join {$table_prefix}_crmentity qcrm on qcrm.crmid = q.quoteid
LINEIDJOIN
".$potFocus->getNonAdminAccessControlQuery('Potentials',$current_user)."
where
{$table_prefix}_crmentity.deleted = 0 and qcrm.deleted = 0
$whereCond";
// add security parameters
$query = $potFocus->listQueryNonAdminChange($query, 'Potentials');
// replace the join with linesid (troubles in the listQueryChange, so do it later)
$query = str_replace('LINEIDJOIN',"
inner join (
select distinct ipr.id, pl.productlineid
from {$table_prefix}_inventoryproductrel ipr
inner join {$table_prefix}_products p on p.productid = ipr.productid
inner join {$table_prefix}_crmentity pcrm on pcrm.crmid = p.productid
left join {$table_prefix}_productlines pl on pl.productlineid = p.productlineid
left join {$table_prefix}_crmentity plcrm on plcrm.crmid = pl.productlineid
where pcrm.deleted = 0 and (plcrm.deleted is null or plcrm.deleted = 0)
) pl on pl.id = q.quoteid",
$query);
// exclude potentials with no or more than 1 active quotes
$respot = $adb->query($query);
$data = array();
$oldid = null;
$remove_ids = array();
while ($row = $adb->FetchByAssoc($respot, -1, false)) {
if (!is_null($oldid) && $oldid == $row['potentialid']) {
$remove_ids[] = $oldid;
continue;
}
$data[$row['potentialid']] = $row;
$oldid = $row['potentialid'];
}
$remove_ids = array_unique($remove_ids);
if (count($remove_ids) > 0) {
foreach ($remove_ids as $rid) {
unset($data[$rid]);
}
}
return $data;
}
function GenerateReport($outputformat, $filterlist = null, $directOutput=false) {
global $adb, $table_prefix;
ini_set('max_execution_time',600);
$data = $this->getData($this->reportid,$filterlist,$outputformat);
$nrows = count($data);
$html = '';
$potFocus = CRMEntity::getInstance('Potentials');
switch ($outputformat) {
default:
case 'HTML':
case 'PDF':
case 'PRINT':
if (count($data) > 0) {
$html = "
";
$html .=
"
| ".($this->budgetPerUser ? getTranslatedString('LBL_USER') : getTranslatedString('SINGLE_ProductLines', 'ProductLines'))." |
Budget |
Best |
Forecast |
Worst |
".getTranslatedString('ClosedOrders')." |
Delta Budget |
";
$rowN = 0;
$counter = 0;
foreach ($data as $drow) {
if ($drow['productlineid'] > 0) {
$prodLineName = getEntityName('ProductLines', $drow['productlineid']);
$prodLineName = $prodLineName[$drow['productlineid']];
$budgetQty = 0;
$budgetDivisors = array('year'=>1, 'months6'=>2, 'months4'=>3, 'months3'=>4, 'month'=>12);
if (array_key_exists($this->budgetPeriod, $budgetDivisors)) {
$yearyAmount = getSingleFieldValue($table_prefix.'_productlines', 'yearly_budget', 'productlineid', $drow['productlineid']);
$budgetQty = floatval($yearyAmount) / $budgetDivisors[$this->budgetPeriod];
}
$budget = formatUserNumber($budgetQty);
$deltaBudget = formatUserNumber($drow['orders_tot'] - $budgetQty); // crmv@49622
} else {
$prodLineName = 'N/A';
$budget = $deltaBudget = ' - ';
}
$best = formatUserNumber($drow['best']);
$forecast = formatUserNumber($drow['forecast']);
$worst = formatUserNumber($drow['worst']);
$orders = formatUserNumber($drow['orders_tot']);
if ($this->budgetPerUser) {
$oppalist = $this->getPotentialsByUser($drow['smownerid'], $potFocus);
$prodLineName = getUserName($drow['smownerid']);
} else {
$oppalist = $this->getPotentialsByProdLine($drow['productlineid'], $potFocus);
}
$oppadiv = '';
if (count($oppalist) > 0) {
$scrolling = (count($oppalist) > 10);
if ($scrolling) {
$oppadiv .= '';
}
$oppadiv .= "
";
if ($scrolling) {
$oppadiv .= '
';
}
$oppadiv = getCrmvDivHtml('potList_'.$counter, getTranslatedString('Potentials').' '.getTranslatedString('LBL_FOR').' '.$prodLineName, $oppadiv);
}
$linename = "$prodLineName$oppadiv";
$html .=
"
| $linename |
$budget |
$best |
$forecast |
$worst |
$orders |
$deltaBudget |
";
$rowN ^= 1;
++$counter;
}
$html .= "
";
} else {
$html = "".getTranslatedString('LBL_NO_DATA')."";
}
$html .= ''; // crmv@49622
$return_data = $html;
break;
case 'XLS':
$return_data = '';
break;
case 'TOTALXLS':
case 'TOTALHTML':
case 'PRINT_TOTAL':
$report_data = '';
break;
}
return array($return_data, $nrows);
}
}
?>