* SPDX-License-Identifier: AGPL-3.0-only ************************************/ require_once('include/Webservices/Utils.php'); require_once('include/Webservices/DescribeObject.php'); require_once('modules/Reports/ScheduledReports.php'); // crmv@139057 /* crmv@97862 crmv@100905 crmv@100399 */ class Reports extends SDKExtendableClass { var $tab_name = array(); // keep this for WS compatibility var $column_fields = Array(); var $sort_values = Array(); var $primodule; var $secmodule; var $columnssummary; var $columnscountsummary = array(); // crmv@29686 var $folderid; var $adv_rel_fields = Array(); var $module_list = Array(); public $max_relation_levels = 5; // crmv@154814 public $max_grouping_levels = 7; public $enable_clusters = true; // crmv@128369 /* IDs for fake fields and blocks */ /* deprecated */ protected $maxTabid = 200; protected $tabidPB = 200; protected $baseFieldIdPB = 10000; protected $baseBlockIdPB = 10000; protected $baseTaxFieldId = 20000; protected $baseTaxBlockId = 20000; /* internal caches */ static protected $subuser_cache = array(); static protected $groups_cache = array(); static protected $viewable_cache = array(); static protected $editable_cache = array(); static protected $exportable_cache = array(); static protected $report_cache = array(); // crmv@38798 // various functions that can be applied to columns to alter the extracted value // uitypes are not used yet, but datatype is (see Reports.js) // parameters are {column} {param1} {param2} var $db_functions = array( 'extract_year' => array( 'label' => 'LBL_REP_EXTRACT_YEAR', 'uitypes' => array(5,6,70), 'wstypes' => array('date', 'datetime'), 'sql' => array( 'mysql' => 'EXTRACT(YEAR FROM {column})', 'mssql' => 'DATEPART(YEAR, {column})', 'oracle'=> 'EXTRACT(YEAR FROM {column})', ), ), 'extract_quarter' => array( 'label' => 'LBL_REP_EXTRACT_QUARTER', 'uitypes' => array(5,6,70), 'wstypes' => array('date', 'datetime'), 'sql' => array( 'mysql' => 'EXTRACT(QUARTER FROM {column})', 'mssql' => 'DATEPART(QUARTER, {column})', 'oracle'=> 'TO_CHAR({column}, \'Q\')', ), ), 'extract_yearmonth' => array( 'label' => 'LBL_REP_EXTRACT_YEARMONTH', 'uitypes' => array(5,6,70), 'wstypes' => array('date', 'datetime'), 'sql' => array( 'mysql' => 'DATE_FORMAT({column}, \'%Y-%m\')', 'mssql' => 'CONVERT(CHAR(7), {column}, 120)', 'oracle'=> 'TO_CHAR({column}, \'YYYY-MM\')', ), ), 'extract_month' => array( 'label' => 'LBL_REP_EXTRACT_MONTH', 'uitypes' => array(5,6,70), 'wstypes' => array('date', 'datetime'), 'sql' => array( 'mysql' => 'EXTRACT(MONTH FROM {column})', 'mssql' => 'DATEPART(MONTH, {column})', 'oracle'=> 'EXTRACT(MONTH FROM {column})', ), ), 'extract_week' => array( 'label' => 'LBL_REP_EXTRACT_WEEK', 'uitypes' => array(5,6,70), 'wstypes' => array('date', 'datetime'), 'sql' => array( 'mysql' => 'EXTRACT(WEEK FROM {column})', 'mssql' => 'DATEPART(WEEK, {column})', 'oracle'=> 'TO_CHAR({column}, \'WW\')', ), ), 'extract_day' => array( 'label' => 'LBL_REP_EXTRACT_DAY', 'uitypes' => array(5,6,70), 'wstypes' => array('date', 'datetime'), 'sql' => array( 'mysql' => 'EXTRACT(DAY FROM {column})', 'mssql' => 'DATEPART(DAY, {column})', 'oracle'=> 'EXTRACT(DAY FROM {column})', ), ), 'extract_date' => array( 'label' => 'LBL TCDate', 'uitypes' => array(70), 'wstypes' => array('datetime'), 'sql' => array( 'mysql' => 'DATE({column})', 'mssql' => 'CONVERT(date, {column})', 'oracle'=> 'TO_CHAR({column}, \'YYYY-MM-DD\')', ), ), ); // crmv@38798e public function __construct() { // nothing at the moment } public function getSubordinateUsers($userid = null) { global $current_user; if (!$userid) $userid = $current_user->id; if (!isset(self::$subuser_cache[$userid])) { $subordinate_users = Array(); $user_array = getRoleAndSubordinateUsers($current_user->roleid,true); foreach ($user_array as $userid => $username) { $subordinate_users[] = array( // crmv@127805 'userid' => $userid, 'username' => $username, 'label' => $username, 'value' => "users::$userid", ); } usort($subordinate_users, function($a, $b) { // crmv@127805 return strcasecmp($a['label'], $b['label']); }); self::$subuser_cache[$userid] = $subordinate_users; } return self::$subuser_cache[$userid]; } // crmv@127805 public function getSubordinateUsersIds($userid = null) { $usersById = array(); $list = $this->getSubordinateUsers($userid); foreach ($list as $user) { $usersById[$user['userid']] = $user['username']; } return $usersById; } // crmv@127805e public function getUserGroups($userid = null) { global $current_user; if (!$userid) $userid = $current_user->id; if (!isset(self::$groups_cache[$userid])) { $userGroups = new GetUserGroups(); $userGroups->getAllUserGroups($userid); $user_groups = array(); foreach ($userGroups->user_groups as $groupid) { $ginfo = getGroupDetails($groupid); $user_groups[$groupid] = array( 'groupid' => $groupid, 'groupname' => $ginfo[1], 'label' => $ginfo[1], 'value' => "groups::$groupid", ); } uasort($user_groups, function($a, $b) { return strcasecmp($a['label'], $b['label']); }); self::$groups_cache[$userid] = $user_groups; } return self::$groups_cache[$userid]; } public function getModuleLabel($module) { if ($module == 'Calendar') { $trans = getTranslatedString('Tasks', 'APP_STRINGS'); // crmv@127526 } elseif (FakeModules::isFakeModule($module)) { $trans = FakeModules::getModuleLabel($module); // crmv@127526e } else { $trans = getTranslatedString($module,$module); } return $trans; } public function getAvailableModules() { global $adb, $table_prefix; if (empty($this->module_list)) { $modules = Array(); $restricted_tabs = getHideTab('hide_report'); //crmv@27711 // get available modules if (is_array($restricted_tabs) && count($restricted_tabs) > 0) { $res = $adb->pquery("SELECT tabid,name FROM {$table_prefix}_tab WHERE presence IN (0,2) AND isentitytype = 1 AND tabid NOT IN (".generateQuestionMarks($restricted_tabs).")", $restricted_tabs); } else { $res = $adb->query("SELECT tabid,name FROM {$table_prefix}_tab WHERE presence IN (0,2) AND isentitytype = 1"); } while ($row = $adb->FetchByAssoc($res, -1, false)) { $module = $row['name']; if (isPermitted($module,'index') == "yes") { $modules[$module] = $this->getModuleLabel($module); // add fake product block module if (isInventoryModule($module)) { $modules['ProductsBlock'] = getTranslatedString('LBL_RELATED_PRODUCTS', 'Settings'); } } } // sort by name asort($modules); $this->module_list = $modules; } return $this->module_list; } // crmv@38798 function get_available_functions() { $ret = array(); foreach ($this->db_functions as $fkey => $finfo) { $ret[] = array( 'name'=>$fkey, 'label'=>getTranslatedString($finfo['label'], 'Reports'), 'uitypes'=> $finfo['uitypes'], 'wstypes'=> $finfo['wstypes'], ); } return $ret; } // crmv@38798e /** Function to get the Listview of Reports * This function accepts no argument * This generate the Reports view page and returns a string * contains HTML */ function sgetRptFldr($mode='', $folderid = null, $special_request=false) // crmv@30967 crmv@163922 { global $adb,$log,$mod_strings,$table_prefix; $returndata = Array(); // crmv@30967 $params = array($this->getTabId('Reports')); $sql = "select * from ".$table_prefix."_crmentityfolder where tabid = ? "; if (!is_null($folderid) && $folderid > 0) { $sql .= ' and folderid = ? '; $params[] = $folderid; } // crmv@163922 if ($special_request !== false){ switch($special_request){ case 'getAllButSDKFolders': $sql .= ' and state <> ? '; $params[] = "SDK"; $sql .= ' order by foldername'; $result = $adb->pquery($sql, $params); while ($reportfldrow = $adb->fetch_array($result)) { $details = Array(); $details['state'] = $reportfldrow["state"]; $details['id'] = $reportfldrow["folderid"]; $details['name'] = ($mod_strings[$reportfldrow["foldername"]] == '' ) ? $reportfldrow["foldername"]:$mod_strings[$reportfldrow["foldername"]]; $details['description'] = $reportfldrow["description"]; $details['fname'] = popup_decode_html($details['name']); $details['fdescription'] = popup_decode_html($reportfldrow["description"]); $returndata[] = $details; } return $returndata; break; } } // crmv@163922e $sql .= ' order by foldername'; $result = $adb->pquery($sql, $params); // Fetch details of all reports of folder at once $reportsInAllFolders = $this->sgetRptsforFldr($folderid > 0 ? $folderid : false); // crmv@163922 // crmv@30967e $reportfldrow = $adb->fetch_array($result); if($mode != '') { do { if ((is_array($mode) && in_array($reportfldrow["state"], $mode)) || ($mode == $reportfldrow["state"])) { $details = Array(); $details['state'] = $reportfldrow["state"]; $details['id'] = $reportfldrow["folderid"]; $details['name'] = ($mod_strings[$reportfldrow["foldername"]] == '' ) ? $reportfldrow["foldername"]:$mod_strings[$reportfldrow["foldername"]]; $details['name'] = html_entity_decode($details['name']); // crmv@169262 $details['description'] = $reportfldrow["description"]; $details['fname'] = popup_decode_html($details['name']); $details['fdescription'] = popup_decode_html($reportfldrow["description"]); // crmv@163922 if ($folderid > 0) { $details['details'] = $reportsInAllFolders; } else { $details['details'] = $reportsInAllFolders[$reportfldrow["folderid"]]; } // crmv@163922e $returndata[] = $details; } }while($reportfldrow = $adb->fetch_array($result)); }else { do { $details = Array(); $details['state'] = $reportfldrow["state"]; $details['id'] = $reportfldrow["folderid"]; $details['name'] = ($mod_strings[$reportfldrow["foldername"]] == '' ) ? $reportfldrow["foldername"]:$mod_strings[$reportfldrow["foldername"]]; $details['name'] = html_entity_decode($details['name']); // crmv@169262 $details['description'] = $reportfldrow["description"]; $details['fname'] = popup_decode_html($details['name']); $details['fdescription'] = popup_decode_html($reportfldrow["description"]); // crmv@163922 if ($folderid > 0) { $details['details'] = $reportsInAllFolders; } else { $details['details'] = $reportsInAllFolders[$reportfldrow["folderid"]]; } // crmv@163922e $returndata[] = $details; }while($reportfldrow = $adb->fetch_array($result)); } $log->info("Reports :: ListView->Successfully returned vte_report folder HTML"); return $returndata; } // crmv@38798 - overridden function countAllRecordsInFolder($module, $folderid) { global $adb, $table_prefix; // find columnname $fieldinfo = array('columnname' => 'folderid', 'tablename'=>$table_prefix.'_report'); $res = $adb->pquerySlave('Reports',"select count(*) as cnt from {$fieldinfo['tablename']} where {$fieldinfo['columnname']} = ?", array($folderid)); // crmv@185894 if ($res) { return $adb->query_result_no_html($res, 0, 'cnt'); } return false; } // crmv@38798e // crmv@30967 crmv@163922 function getFolderContent($folderid) { $count = $this->sgetRptsforFldr($folderid,false,true); return array('count'=>$count, 'html'=>''); } // crmv@163922e function getFolderList() { $flds = getEntityFoldersByName(null, 'Reports'); // translate folder names foreach ($flds as $k=>$fold) { $fold['foldername'] = html_entity_decode($fold['foldername']); // crmv@169262 $flds[$k]['foldername'] = getTranslatedString($fold['foldername'], 'Reports'); $flds[$k]['description'] = getTranslatedString($fold['description'], 'Reports'); } return $flds; } // crmv@30967e /** Function to get the Reports inside each modules * This function accepts the folderid * This Generates the Reports under each Reports module * This Returns a HTML sring */ function sgetRptsforFldr($rpt_fldr_id,$module=false,$onlycount=false) //crmv@31775 crmv@163922 { global $log, $adb, $table_prefix; global $current_user; static $cached_perm = Array(); // crmv@163922 if ($module && !is_array($module)) $module = array($module); // crmv@159603 $returndata = Array(); require_once('include/utils/UserInfoUtil.php'); $sql = "SELECT r.*, rc.module, cf.folderid, cf.foldername FROM {$table_prefix}_report r LEFT JOIN {$table_prefix}_reportconfig rc ON rc.reportid = r.reportid INNER JOIN {$table_prefix}_crmentityfolder cf on cf.folderid = r.folderid"; $params = array(); // If information is required only for specific report folder? if($rpt_fldr_id !== false) { $sql .= " WHERE cf.folderid = ?"; $params[] = $rpt_fldr_id; $haswhere = true; } require('user_privileges/requireUserPrivileges.php'); require_once('include/utils/GetUserGroups.php'); $userGroups = new GetUserGroups(); $userGroups->getAllUserGroups($current_user->id); $user_groups = $userGroups->user_groups; if(!empty($user_groups) && $is_admin==false){ $user_group_query = " (shareid IN (".generateQuestionMarks($user_groups).") AND setype='groups') OR"; array_push($params, $user_groups); } $non_admin_query = " r.reportid IN (SELECT reportid from ".$table_prefix."_reportsharing WHERE $user_group_query (shareid=? AND setype='users'))"; if($is_admin==false){ $sql .= ($haswhere ? ' and ' : ' where ')." ( (".$non_admin_query.") or r.sharingtype='Public' or r.owner = ? or r.owner in(select ".$table_prefix."_user2role.userid from ".$table_prefix."_user2role inner join ".$table_prefix."_users on ".$table_prefix."_users.id=".$table_prefix."_user2role.userid inner join ".$table_prefix."_role on ".$table_prefix."_role.roleid=".$table_prefix."_user2role.roleid where ".$table_prefix."_role.parentrole like '".$current_user_parent_role_seq."::%'))"; array_push($params, $current_user->id); array_push($params, $current_user->id); $haswhere = true; //crmv@31775 } $query = $adb->pquery("select userid from ".$table_prefix."_user2role inner join ".$table_prefix."_users on ".$table_prefix."_users.id=".$table_prefix."_user2role.userid inner join ".$table_prefix."_role on ".$table_prefix."_role.roleid=".$table_prefix."_user2role.roleid where ".$table_prefix."_role.parentrole like '".$current_user_parent_role_seq."::%'",array()); $subordinate_users = Array(); for($i=0;$i<$adb->num_rows($query);$i++){ $subordinate_users[] = $adb->query_result($query,$i,'userid'); } // crmv@163922 if ($onlycount){ $result = $adb->pquerySlave('Reports',$sql, $params); // crmv@185894 return $adb->num_rows($result); } // crmv@163922e // order reports by name $sql .= " ORDER BY r.reportname"; $result = $adb->pquery($sql, $params); while ($report = $adb->FetchByAssoc($result, -1, false)) { // crmv@169289 $modules = $this->getAllModules($report["reportid"]); if (is_array($module) && count($module) > 0 && count(array_intersect($module, $modules)) == 0) continue; // crmv@159603 $report_details = Array(); $report_details ['customizable'] = $report["customizable"]; $report_details ['reportid'] = $report["reportid"]; $report_details ['folderid'] = $report["folderid"]; $report_details ['owner'] = getUserName($report["owner"]); $report_details ['module'] = $report["module"]; $report_details ['primarymodule'] = $report["module"]; // kept for compatibility $report_details ['modules'] = $modules; $report_details ['state'] = $report["state"]; $report_details ['description'] = $report["description"]; $report_details ['reportname'] = $report["reportname"]; $report_details ['sharingtype'] = $report["sharingtype"]; $report_details ['foldername'] = $report["foldername"]; // crmv@30967 if($is_admin==true || in_array($report["owner"],$subordinate_users) || $report["owner"]==$current_user->id) $report_details ['editable'] = 'true'; else $report_details['editable'] = 'false'; // crmv@163922 if (!isset($cached_perm[$report["module"]])) { $cached_perm[$report["module"]] = (isPermitted($report["module"],'index') == 'yes') ? true : false; } if ($cached_perm[$report["module"]]) { $returndata[$report["folderid"]][] = $report_details; } // crmv@163922e } // crmv@30967 //crmv@31775 if($module === false) { if($rpt_fldr_id !== false) { $returndata = $returndata[$rpt_fldr_id]; } } // crmv@30967e //crmv@31775e $log->info("Reports :: ListView->Successfully returned vte_report details HTML"); return $returndata; } // crmv@67929 function generateTaxNames() { global $table_prefix; if (empty($this->taxNames) || empty($this->taxProdNames)) { $IUtils = InventoryUtils::getInstance(); $allTaxes = $IUtils->getAllTaxes('all'); $taxnames = array(); $taxprodnames = array(); foreach ($allTaxes as $tax) { $taxnames[$tax['taxname']] = getTranslatedString('LBL_TAX').' ('.$tax['taxlabel'].')'; $taxprodnames[$tax['taxname']] = getTranslatedString('LBL_TAX').' '.getTranslatedString('LBL_PRODUCT').' ('.$tax['taxlabel'].')'; } $taxnames['tax_total'] = getTranslatedString('LBL_TAX').' ('.getTranslatedString('LBL_TOTAL').')'; $taxprodnames['tax_total'] = getTranslatedString('LBL_TAX').' '.getTranslatedString('LBL_PRODUCT').' ('.getTranslatedString('LBL_TOTAL').')'; $this->taxNames = $taxnames; $this->taxProdNames = $taxprodnames; } } // crmv@67929e public function getStdFilterOptions($reportid, $selected = '') { $customview = CRMEntity::getInstance('CustomView'); // crmv@115329 $opts = $customview->getStdFilterCriteria($selected); return $opts; } /** * Get a list of all the fields usable for standard filters * Valid fields are those choosable among all the involved modules */ public function getStdFilterFields($reportid) { $config = $this->loadReport($reportid); $allfields = array(); $allmods = $this->getAllModules($reportid); $chains = $this->getAllChains($reportid); foreach ($chains as $chainmod) { $list = $this->getStdFiltersFieldsListForChain($reportid, $chainmod['chain']); if (is_array($list)) { foreach($list as &$group) { foreach ($group['fields'] as &$fld) { $fld['label'] = $this->getModuleLabel($fld['module']) .' - ' . $fld['label']; } unset($fld); } unset($group); } $allfields = array_merge($allfields, $list); // crmv@128192 } return $allfields; } /** Function to form a javascript to determine the start date and end date for a standard filter * This function is to form a javascript to determine * the start date and End date from the value selected in the combo lists */ function getCriteriaJS() { global $current_user; // crmv@150808 static $dayNames = array('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'); // get first and last day of week $weekstart = $current_user->weekstart; if ($weekstart === null || $weekstart === '') $weekstart = 1; $weekstart = intval($weekstart); $weekend = ($weekstart + 6) % 7; // crmv@150808e $today = date("Y-m-d",mktime(0, 0, 0, date("m") , date("d"), date("Y"))); $tomorrow = date("Y-m-d",mktime(0, 0, 0, date("m") , date("d")+1, date("Y"))); $yesterday = date("Y-m-d",mktime(0, 0, 0, date("m") , date("d")-1, date("Y"))); $currentmonth0 = date("Y-m-d",mktime(0, 0, 0, date("m"), "01", date("Y"))); $currentmonth1 = date("Y-m-t"); $lastmonth0 = date("Y-m-d",mktime(0, 0, 0, date("m")-1, "01", date("Y"))); //crmv@50067 //$lastmonth1 = date("Y-m-t", strtotime("-1 Month")); $lastmonth1 = date("Y-m-t", strtotime($lastmonth0)); //crmv@50067e $nextmonth0 = date("Y-m-d",mktime(0, 0, 0, date("m")+1, "01", date("Y"))); $nextmonth1 = date("Y-m-t", strtotime("+1 Month")); // crmv@150808 $todayNum = date('w'); $prevstart = ($todayNum == $weekstart ? time() : strtotime("last {$dayNames[$weekstart]}")); $nextend = ($todayNum == $weekend ? time() : strtotime("next {$dayNames[$weekend]}")); $lastweek0 = date('Y-m-d', strtotime('-1 week', $prevstart)); $lastweek1 = date('Y-m-d', strtotime('-1 week', $nextend)); $thisweek0 = date('Y-m-d', $prevstart); $thisweek1 = date('Y-m-d', $nextend); $nextweek0 = date('Y-m-d', strtotime('+1 week', $prevstart)); $nextweek1 = date('Y-m-d', strtotime('+1 week', $nextend)); // crmv@150808e $next7days = date("Y-m-d",mktime(0, 0, 0, date("m") , date("d")+6, date("Y"))); $next30days = date("Y-m-d",mktime(0, 0, 0, date("m") , date("d")+29, date("Y"))); $next60days = date("Y-m-d",mktime(0, 0, 0, date("m") , date("d")+59, date("Y"))); $next90days = date("Y-m-d",mktime(0, 0, 0, date("m") , date("d")+89, date("Y"))); $next120days = date("Y-m-d",mktime(0, 0, 0, date("m") , date("d")+119, date("Y"))); $last7days = date("Y-m-d",mktime(0, 0, 0, date("m") , date("d")-6, date("Y"))); $last30days = date("Y-m-d",mktime(0, 0, 0, date("m") , date("d")-29, date("Y"))); $last60days = date("Y-m-d",mktime(0, 0, 0, date("m") , date("d")-59, date("Y"))); $last90days = date("Y-m-d",mktime(0, 0, 0, date("m") , date("d")-89, date("Y"))); $last120days = date("Y-m-d",mktime(0, 0, 0, date("m") , date("d")-119, date("Y"))); $currentFY0 = date("Y-m-d",mktime(0, 0, 0, "01", "01", date("Y"))); $currentFY1 = date("Y-m-t",mktime(0, 0, 0, "12", date("d"), date("Y"))); $lastFY0 = date("Y-m-d",mktime(0, 0, 0, "01", "01", date("Y")-1)); $lastFY1 = date("Y-m-t", mktime(0, 0, 0, "12", date("d"), date("Y")-1)); $nextFY0 = date("Y-m-d",mktime(0, 0, 0, "01", "01", date("Y")+1)); $nextFY1 = date("Y-m-t", mktime(0, 0, 0, "12", date("d"), date("Y")+1)); if(date("m") <= 3) { $cFq = date("Y-m-d",mktime(0, 0, 0, "01","01",date("Y"))); $cFq1 = date("Y-m-d",mktime(0, 0, 0, "03","31",date("Y"))); $nFq = date("Y-m-d",mktime(0, 0, 0, "04","01",date("Y"))); $nFq1 = date("Y-m-d",mktime(0, 0, 0, "06","30",date("Y"))); $pFq = date("Y-m-d",mktime(0, 0, 0, "10","01",date("Y")-1)); $pFq1 = date("Y-m-d",mktime(0, 0, 0, "12","31",date("Y")-1)); }else if(date("m") > 3 and date("m") <= 6) { $pFq = date("Y-m-d",mktime(0, 0, 0, "01","01",date("Y"))); $pFq1 = date("Y-m-d",mktime(0, 0, 0, "03","31",date("Y"))); $cFq = date("Y-m-d",mktime(0, 0, 0, "04","01",date("Y"))); $cFq1 = date("Y-m-d",mktime(0, 0, 0, "06","30",date("Y"))); $nFq = date("Y-m-d",mktime(0, 0, 0, "07","01",date("Y"))); $nFq1 = date("Y-m-d",mktime(0, 0, 0, "09","30",date("Y"))); }else if(date("m") > 6 and date("m") <= 9) { $nFq = date("Y-m-d",mktime(0, 0, 0, "10","01",date("Y"))); $nFq1 = date("Y-m-d",mktime(0, 0, 0, "12","31",date("Y"))); $pFq = date("Y-m-d",mktime(0, 0, 0, "04","01",date("Y"))); $pFq1 = date("Y-m-d",mktime(0, 0, 0, "06","30",date("Y"))); $cFq = date("Y-m-d",mktime(0, 0, 0, "07","01",date("Y"))); $cFq1 = date("Y-m-d",mktime(0, 0, 0, "09","30",date("Y"))); } else if(date("m") > 9 and date("m") <= 12) { $nFq = date("Y-m-d",mktime(0, 0, 0, "01","01",date("Y")+1)); $nFq1 = date("Y-m-d",mktime(0, 0, 0, "03","31",date("Y")+1)); $pFq = date("Y-m-d",mktime(0, 0, 0, "07","01",date("Y"))); $pFq1 = date("Y-m-d",mktime(0, 0, 0, "09","30",date("Y"))); $cFq = date("Y-m-d",mktime(0, 0, 0, "10","01",date("Y"))); $cFq1 = date("Y-m-d",mktime(0, 0, 0, "12","31",date("Y"))); } // crmv@192261 $sjsStr = ''; // crmv@192261e return $sjsStr; } //crmv@26161 function getAdvFilterOptions() { $adv_filter_options = array( "e" => getTranslatedString('equals', 'CustomView'), "n" => getTranslatedString('not equal to', 'CustomView'), "s" => getTranslatedString('starts with', 'CustomView'), "ew"=> getTranslatedString('ends with', 'CustomView'), "c" => getTranslatedString('contains', 'CustomView'), "k" => getTranslatedString('does not contain', 'CustomView'), "l" => getTranslatedString('less than', 'CustomView'), "g" => getTranslatedString('greater than', 'CustomView'), "m" => getTranslatedString('less or equal', 'CustomView'), "h" => getTranslatedString('greater or equal', 'CustomView'), "bw" => getTranslatedString('between', 'CustomView'), "a" => getTranslatedString('after', 'CustomView'), "b" => getTranslatedString('before', 'CustomView'), ); array_walk($adv_filter_options, function(&$label) { $label = strtolower($label); }); return $adv_filter_options; } //crmv@26161e //crmv@3085m function getEntityPreview($id,$module='Reports') { $this->Reports($id); $name = $this->reportname; $preview = array( 'id'=>$id, 'module'=>'Reports', 'name'=>$name, 'onclick'=>"location.href='index.php?module=Reports&action=SaveAndRun&tab=Charts&record=$id';", ); $details = array(); $preview['details'] = $details; return $preview; } //crmv@3085me public function isViewable($reportid) { global $current_user; $userid = $current_user->id; if (!isset(self::$viewable_cache[$reportid][$userid])) { $config = $this->loadReport($reportid); $owner = $config['owner']; $sharingType = $config['sharingtype']; $permitted = true; require('user_privileges/requireUserPrivileges.php'); if (!$is_admin && $sharingType != 'Public' && $owner != $userid) { if ($sharingType == 'Private') { // I'm not admin and it's not mine, nobody else can see it // TODO: maybe upper users in the role chain?? $permitted = false; } elseif ($sharingType == 'Shared') { // shared $subusers = $this->getSubordinateUsersIds($userid); // crmv@127805 $user_groups = $this->getUserGroups($userid); if (array_key_exists($owner, $subusers)) { $permitted = true; } elseif (is_array($config['sharing'])) { // check the sharing $permitted = false; foreach ($config['sharing'] as $share) { if ($share['setype'] == 'users' && $share['shareid'] == $userid) { // ok $permitted = true; break; } elseif ($share['setype'] == 'groups' && array_key_exists($share['shareid'], $user_groups)) { // ok $permitted = true; break; } } } else { $permitted = false; } } } // check single modules if (!$is_admin && $permitted) { $modules = $this->getAllModules($reportid); foreach ($modules as $mod) { if (FakeModules::isFakeModule($mod)) continue; //crmv@129274 if (!vtlib_isModuleActive($mod) || isPermitted($mod,'index') != 'yes') { $permitted = false; break; } } } self::$viewable_cache[$reportid][$userid] = $permitted; } return self::$viewable_cache[$reportid][$userid]; } public function isEditable($reportid, $userid = null) { global $current_user; if (!$userid) $userid = $current_user->id; if (!isset(self::$editable_cache[$reportid][$userid])) { $config = $this->loadReport($reportid); $subusers = $this->getSubordinateUsersIds($userid); // crmv@127805 $sharing = $config['sharingtype']; require('user_privileges/requireUserPrivileges.php'); if ($is_admin==true || array_key_exists($config["owner"],$subusers) || $config["owner"]==$userid) { self::$editable_cache[$reportid][$userid] = true; } else { self::$editable_cache[$reportid][$userid] = false; } } return self::$editable_cache[$reportid][$userid]; } public function isExportable($reportid) { global $current_user; $userid = $current_user->id; if (!isset(self::$exportable_cache[$reportid][$userid])) { $modules = $this->getAllModules($reportid); $permitted = true; foreach ($modules as $mod) { if (FakeModules::isFakeModule($mod)) continue; //crmv@129274 if (!vtlib_isModuleActive($mod) || isPermitted($mod,'Export') != 'yes') { $permitted = false; break; } } self::$exportable_cache[$reportid][$userid] = $permitted; } return self::$exportable_cache[$reportid][$userid]; } /** * Return all the involved modules for this report */ public function getAllModules($reportid) { $modules = array(); $config = $this->loadReport($reportid); $modules[] = $config['module']; if (is_array($config['relations'])) { foreach ($config['relations'] as $rel) { $mod = $rel['module']; $modules[] = $mod; } } $modules = array_unique($modules); return $modules; } /** * Return all the involved modules and the relation chain for each */ public function getAllChains($reportid) { $modules = array(); $config = $this->loadReport($reportid); $modules[$config['module']] = array( 'module' => $config['module'], 'chain' => array($config['module']), ); if (is_array($config['relations'])) { foreach ($config['relations'] as $rel) { $mod = $rel['module']; if ($mod != 'ProductsBlock') { $modules[$rel['module']] = array( 'module' => $rel['module'], 'relation' => $rel['name'], 'chain' => $this->getChainFromRelations($rel['name'], $config['relations']), ); } } } $modules = array_values($modules); return $modules; } /** * Load data of the specified report from the DB */ public function loadReport($reportid) { global $adb, $table_prefix; if (!self::$report_cache[$reportid]) { $blobs = array('relations', 'fields', 'stdfilters', 'advfilters', 'clusters', 'totals', 'summary', 'scheduling'); // crmv@128369 crmv@139057 $config = null; $res = $adb->pquery("SELECT * FROM {$table_prefix}_report r INNER JOIN {$table_prefix}_reportconfig rc ON r.reportid = rc.reportid WHERE r.reportid = ?", array($reportid)); if ($res && $adb->num_rows($res) > 0) { $config = $adb->fetchByAssoc($res, -1, false); unset($config['reportid']); foreach ($blobs as $column) { if (!empty($config[$column])) { $config[$column] = Zend_Json::decode($config[$column]); } } // add the sharing info if ($config['sharingtype'] == 'Shared') { $sharing = array(); $res = $adb->pquery("SELECT * FROM {$table_prefix}_reportsharing WHERE reportid = ?", array($reportid)); while ($row = $adb->FetchByAssoc($res, -1, false)) { unset($row['reportid']); $row['value'] = $row['setype'].'::'.$row['shareid']; $row['label'] = getOwnerName($row['shareid']); $sharing[] = $row; } // crmv@200819 usort($sharing, function ($item1, $item2) { return $item1['label'] <=> $item2['label']; }); // crmv@200819e $config['sharing'] = $sharing; } } self::$report_cache[$reportid] = $config; } return self::$report_cache[$reportid]; } public function createChart($reportid, $chartinfo) { global $current_user; $focus = CRMEntity::getInstance('Charts'); $focus->mode = ''; $focus->column_fields['assigned_user_id'] = $current_user->id; $focus->column_fields['reportid'] = $reportid; foreach($focus->column_fields as $fieldname => $val) { if(isset($chartinfo[$fieldname])) { $value = trim($chartinfo[$fieldname]); $focus->column_fields[$fieldname] = $value; } } $focus->save('Charts'); return $focus->id; } /** * Checks whether a report with the provided id exists */ public function reportExists($reportid) { global $adb, $table_prefix; $res = $adb->pquery("SELECT r.reportid FROM {$table_prefix}_report r INNER JOIN {$table_prefix}_reportconfig rc ON r.reportid = rc.reportid WHERE r.reportid = ?", array($reportid)); return ($res && $adb->num_rows($res) > 0); } /** * Save the report to DB (update if existing) */ public function saveReport($reportid, $config) { if (!empty($reportid) && $this->reportExists($reportid)) { $r = $this->updateReport($reportid, $config); } else { $r = $this->insertReport($config); } // crmv@139057 - reset scheduling if ($r && $config['scheduling'] && $config['scheduling']['format']) { // crmv@172017 $SR = ScheduledReports::getInstance(); $SR->updateNextExecution($r, $config['scheduling']); // crmv@172017 } // crmv@139057e return $r; } /** * Insert a new report in the db */ public function insertReport($config) { global $adb, $table_prefix, $current_user; $reportid = $adb->getUniqueId($table_prefix.'_report'); $columns1 = array( 'reportid' => $reportid, 'folderid' => intval($config['folderid']), 'reportname' => $config['reportname'], 'description' => $config['description'], 'reporttype' => $config['reporttype'] ?: 'tabular', 'state' => $config['state'] ?: 'CUSTOM', 'customizable' => isset($config['customizable']) ? intval($config['customizable']) : 1, 'owner' => $config['owner'] ?: $current_user->id, 'sharingtype' => $config['sharingtype'] ?: 'Public', ); $columns = array_keys($columns1); $adb->format_columns($columns); $adb->pquery("INSERT INTO {$table_prefix}_report (".implode(',',$columns).") VALUES (".generateQuestionMarks($columns1).")", $columns1); $columns2 = array( 'reportid' => $reportid, 'module' => $config['module'], ); $columns = array_keys($columns2); $adb->format_columns($columns); $adb->pquery("INSERT INTO {$table_prefix}_reportconfig (".implode(',',$columns).") VALUES (".generateQuestionMarks($columns2).")", $columns2); $blobs = array( 'relations' => $config['relations'], 'fields' => $config['fields'], 'stdfilters' => $config['stdfilters'], 'advfilters' => $config['advfilters'], 'clusters' => $config['clusters'], // crmv@128369 'totals' => $config['totals'], 'summary' => $config['summary'], 'scheduling' => $config['scheduling'], // crmv@139057 ); foreach ($blobs as $column => $data) { if (!empty($data)) { $adb->updateClob($table_prefix.'_reportconfig',$column,"reportid = $reportid",Zend_Json::encode($data)); } } // sharing if ($config['sharingtype'] == 'Shared' && is_array($config['sharing'])) { foreach ($config['sharing'] as $share) { $rshare = array( 'reportid' => $reportid, 'shareid' => $share['shareid'], 'setype' => $share['setype'], ); $adb->pquery("INSERT INTO {$table_prefix}_reportsharing (".implode(',', array_keys($rshare)).") VALUES (".generateQuestionMarks($rshare).")", $rshare); } } self::$report_cache[$reportid] = $config; return $reportid; } /** * Update an existing report */ public function updateReport($reportid, $config) { global $adb, $table_prefix; $reportid = intval($reportid); // fields that must be passed always passed $columns1 = array( 'folderid' => intval($config['folderid']), 'reportname' => $config['reportname'], 'description' => $config['description'], ); // optional fields if ($config['reporttype']) $columns1['reporttype'] = $config['reporttype']; if (isset($config['customizable'])) $columns1['customizable'] = intval($config['customizable']); if ($config['state']) $columns1['state'] = $config['state']; if ($config['owner']) $columns1['owner'] = $config['owner']; if ($config['sharingtype']) $columns1['sharingtype'] = $config['sharingtype']; $upd = array(); foreach ($columns1 as $col => $value) { $upd[] = "$col = ?"; } $sql = "UPDATE {$table_prefix}_report SET ".implode(',', $upd)." WHERE reportid = ?"; $adb->pquery($sql, array($columns1, $reportid)); $columns2 = array( 'module' => $config['module'], ); $upd = array(); foreach ($columns2 as $col => $value) { $upd[] = "$col = ?"; } $sql = "UPDATE {$table_prefix}_reportconfig SET ".implode(',', $upd)." WHERE reportid = ?"; $adb->pquery($sql, array($columns2, $reportid)); $blobs = array( 'relations' => $config['relations'], 'fields' => $config['fields'], 'stdfilters' => $config['stdfilters'], 'advfilters' => $config['advfilters'], 'clusters' => $config['clusters'], // crmv@128369 'totals' => $config['totals'], 'summary' => $config['summary'], 'scheduling' => $config['scheduling'], // crmv@139057 ); foreach ($blobs as $column => $data) { if (empty($data)) { $adb->pquery("UPDATE {$table_prefix}_reportconfig SET $column = NULL WHERE reportid = ?", array($reportid)); } else { $adb->updateClob($table_prefix.'_reportconfig',$column,"reportid = $reportid",Zend_Json::encode($data)); } } // sharing $adb->pquery("DELETE FROM {$table_prefix}_reportsharing WHERE reportid = ?", array($reportid)); if ($config['sharingtype'] == 'Shared' && is_array($config['sharing'])) { foreach ($config['sharing'] as $share) { $rshare = array( 'reportid' => $reportid, 'shareid' => $share['shareid'], 'setype' => $share['setype'], ); $adb->pquery("INSERT INTO {$table_prefix}_reportsharing (".implode(',', array_keys($rshare)).") VALUES (".generateQuestionMarks($rshare).")", $rshare); } } self::$report_cache[$reportid] = $config; return $reportid; } public function deleteReport($reportid) { global $adb,$table_prefix; unset(self::$report_cache[$reportid]); $adb->pquery("DELETE FROM ".$table_prefix."_report WHERE reportid = ?", array($reportid)); $adb->pquery("DELETE FROM ".$table_prefix."_reportconfig WHERE reportid = ?", array($reportid)); $adb->pquery("DELETE FROM ".$table_prefix."_reportsharing WHERE reportid = ?", array($reportid)); // crmv@42024 - delete chart and summary data for ($i=1; $i<=$this->max_grouping_levels; ++$i) { $adb->pquery("DELETE FROM vte_rep_count_liv{$i} where reportid = ?", array($reportid)); } $adb->pquery("DELETE FROM vte_rep_count_levels where reportid = ?", array($reportid)); // crmv@185894 require_once('modules/Reports/ReportRun.php'); $oReportRun = ReportRun::getInstance($reportid); $oReportRun->enableCacheDb(); if ($oReportRun->cacheDb != '') { for ($i=1; $i<=$this->max_grouping_levels; ++$i) { $adb->pquerySlave('Reports',"DELETE FROM ".$oReportRun->getLivTable('liv',$i)." where reportid = ?", array($reportid)); } $adb->pquerySlave('Reports',"DELETE FROM ".$oReportRun->getLivTable('levels')." where reportid = ?", array($reportid)); } // crmv@185894e $res = $adb->pquery("SELECT {$table_prefix}_charts.chartid FROM {$table_prefix}_charts inner join {$table_prefix}_crmentity on {$table_prefix}_crmentity.crmid = {$table_prefix}_charts.chartid where deleted = 0 and reportid = ?", array($reportid)); if ($res && $adb->num_rows($res) > 0) { $chartFocus = CRMEntity::getInstance('Charts'); while ($row = $adb->FetchByAssoc($res, -1, false)) { $chid = $row['chartid']; $chartFocus->id = $chid; DeleteEntity('Charts', 'Charts', $chartFocus, $chid, null); } } // crmv@42024e // crmv@121612 crmv@185894 // delete report tables $adbSlave = $adb->getSlaveObject('Reports'); $tables = $adbSlave->get_tables(); if (is_array($tables)) { $prefix = $table_prefix.'_rep_subq_'; foreach ($tables as $table) { if (substr($table, 0, strlen($prefix)) == $prefix) { // ok, it's a report table, check the id $pieces = explode('_', $table); if ($pieces[4] == $reportid) { // ok, it's for this report $adb->querySlave('Reports',"DROP TABLE $table"); } } } } // crmv@121612e crmv@185894e $adb->pquery("UPDATE ".$table_prefix."_customview SET reportid = 0 WHERE reportid = ?", array($reportid)); //crmv@40613 // crmv@150024 // remove the report from dynamic targets $focusTargets = CRMEntity::getInstance('Targets'); $focusTargets->deleteDynamicReport($reportid); // crmv@150024e } // crmv@138170 /** * Remove old temporary tables for non existing reports/users */ public function cleanOldTables() { global $adb, $table_prefix; // get list of reportids $reports = array(); $res = $adb->query("SELECT reportid FROM {$table_prefix}_report"); while ($row = $adb->fetchByAssoc($res, -1, false)) { $reports[] = intval($row['reportid']); } // get list of active userids $users = array(); $res = $adb->pquery("SELECT id FROM {$table_prefix}_users WHERE deleted = 0 AND status = ?", array('Active')); while ($row = $adb->fetchByAssoc($res, -1, false)) { $users[] = intval($row['id']); } // get list of tables // crmv@185894 $adbSlave = $adb->getSlaveObject('Reports'); $tables = $adbSlave->get_tables() ?: array(); // crmv@185894e // check for report tables foreach ($tables as $table) { if (preg_match("/^{$table_prefix}_rep_subq_([0-9]+)_([0-9]+)/", $table, $matches)) { $userid = intval($matches[1]); $reportid = intval($matches[2]); if (!in_array($userid, $users) || !in_array($reportid, $reports)) { // remove the table! $adb->querySlave('Reports',"DROP TABLE $table"); // crmv@185894 } } } } // crmv@138170e // crmv@101691 /** * Remove a field from all the reports */ public function deleteFieldFromAll($fieldid) { global $adb, $table_prefix; $sql = "SELECT r.reportid FROM {$table_prefix}_report r INNER JOIN {$table_prefix}_reportconfig rc ON rc.reportid = r.reportid WHERE r.state != 'SDK'"; $res = $adb->query($sql); if ($res && $adb->num_rows($res) > 0) { while ($row = $adb->FetchByAssoc($res, -1, false)) { $this->deleteFieldFromReport($row['reportid'], $fieldid); } } } /** * Remove a field from a report */ public function deleteFieldFromReport($reportid, $fieldid) { $config = $this->loadReport($reportid); $newFields = array(); foreach ($config['fields'] as $field) { if ($field['fieldid'] != $fieldid) { $newFields[] = $field; } } $config['fields'] = $newFields; // stdfilters if (is_array($config['stdfilters'])) { $newStdFilters = array(); foreach ($config['stdfilters'] as $cond) { if ($cond['fieldid'] != $fieldid) { $newStdFilters[] = $cond; // crmv@126285 } } $config['stdfilters'] = $newStdFilters; } // advanced filters if (is_array($config['advfilters'])) { $newAdvFilters = array(); foreach ($config['advfilters'] as $group) { if ($group['conditions']) { $newConds = array(); foreach ($group['conditions'] as $cond) { if ($cond['fieldid'] != $fieldid && (empty($cond['ref_fieldid']) || $cond['ref_fieldid'] != $fieldid)) { $newConds[] = $cond; } } $group['conditions'] = $newConds; $newAdvFilters[] = $group; } } $config['advfilters'] = $newAdvFilters; } // totals and summary if (is_array($config['totals'])) { $newTotals = array(); foreach ($config['totals'] as $field) { if ($field['fieldid'] != $fieldid) { $newTotals[] = $field; } } $config['totals'] = $newTotals; } // now save it again return $this->updateReport($reportid, $config); } // crmv@101691e public function getRelations($reportid) { $config = $this->loadReport($reportid); return $config['relations']; /* Format of Relations: array( array( 'name' => 'Contacts', // name of the relation, if it's the root, then the module name 'module' => 'Contacts', // module of the relation, can be the special ProductsBlock 'parent' => null, // parent relation, null for the root ), array( // this is a NtoN relation 'name' => 'Contacts_Products_rel_21', // relation name, which sould be [Module1]_[Module2]_rel_[RelationID] 'module' => 'Products', // module 'parent' => 'Contacts', // name of the parent relation 'type' => ModuleRelation::$TYPE_NTON, // type of the relation, should be NTON 'relationid' => 21, // id of the relation, (can be a fake id) ), array( 'name' => 'Contacts_Accounts_fld_75', // for 1-N or N-1 the last part is _fld_[FIELDID] 'module' => 'Accounts', 'parent' => 'Contacts', 'type' => ModuleRelation::$TYPE_NTO1, // here is NTO1 or 1TON 'fieldid' => 75, // here the fieldid must be specified, the relationid is not used ), ); */ } public function getColumns($reportid) { $config = $this->loadReport($reportid); return $config['fields']; /* Format of Fields: array( array( 'fieldid' => 449, // basic form, just the fieldid // if no parent is specified, the root module is used ), array( 'fieldid' => 453, 'parent' => 'Contacts_Accounts_fld_75', // the name of the parent relation ), /*array( 'fieldid' => 22, 'group' => true, // group by this field 'sortorder' => 'ASC', // with this order 'summary' => true, // show it in summary as well ), array( 'module' => 'Events', 'fieldid' => 268, 'formula' => 'extract_year', // apply a formula to the field ), ); */ } public function getTotalColumns($reportid) { $config = $this->loadReport($reportid); return $config['totals']; /* Format of Totals: array( array( 'fieldid' => 9, // fieldid 'relation' => null, // you can also specify the relation 'aggregator' => 'SUM', // which formula to use, to use more than one, specify the field more than once ), ); */ } public function getSummaryColumns($reportid) { $config = $this->loadReport($reportid); return $config['summary']; /* Format of Summary columns: You can have maximum one column here array( array( 'fieldid' => 9, // the fieldid 'aggregators' => array('SUM', 'MAX', 'MIN'), // the aggregator formulas to use ), ); */ } public function getStdFilters($reportid) { $config = $this->loadReport($reportid); return $config['stdfilters']; /* Format of Std filters: array( array( 'fieldid' => 98, // fieldid 'relation' => null, // you can specify the relation 'type' => 'datefilter', // type of the std filter, only "datefilter" is supported 'value' => 'thisfy', // value for the datefilter 'startdate' => '', // if custom, here is the start date 'enddate' => '', // and end date ) ); */ } public function getAdvFilters($reportid) { $config = $this->loadReport($reportid); return $config['advfilters']; /* Format of advanced filters: $advfilters = array( // groups array( 'conditions' => array( // conditions for this group array( 'fieldid' => 70, // fieldid 'comparator' => 'c', // comparator 'value' => 'b', // value to compare with 'glue' => 'or' // glue condition for the next field, you can omit if it's the last field ), array( 'fieldid' => 90, 'comparator' => 'e', 'value' => 'admin', ), ), 'glue' => 'and', // glue for the next group ), array( 'conditions' => array( array( 'fieldid' => 78, 'comparator' => 'k', 'reference' => true, // this is a reference comparison, no need to use "value" 'ref_fieldid' => 123, // but you should specify the comparison fieldid 'ref_relation' => 'ddfgdfg' // and relation name ), ), ), ); */ } // crmv@128369 public function getClusters($reportid) { $config = $this->loadReport($reportid); return $config['clusters']; /* Format of clusters: $clusters = array( array( 'name' => 'clustername', // name for the cluster 'color' => '#rrbbgg', // color 'conditions' => array(), // conditions, same format of advanced filters ), ); */ } // crmv@128369e /** * Prepare the report config for the edit panel, adding necessary fields and other informations */ public function prepareForEdit(&$config) { // fields foreach ($config['fields'] as &$field) { $finfo = $this->getFieldInfoById($field['fieldid']); if ($finfo['label']) { $field['label'] = $finfo['label']; } else { $field['label'] = getTranslatedString($finfo['fieldlabel'], $finfo['module']); } // value used in the