Skip to content

Commit

Permalink
Fix contribution detail report to work with FULL GROUP BY mode
Browse files Browse the repository at this point in the history
  • Loading branch information
eileenmcnaughton committed Apr 6, 2018
1 parent da0c485 commit ee56e91
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 62 deletions.
90 changes: 84 additions & 6 deletions CRM/Report/Form.php
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,10 @@ class CRM_Report_Form extends CRM_Core_Form {
*/
protected $addPaging = TRUE;

protected $isForceGroupBy = FALSE;

protected $groupConcatTested = FALSE;

/**
* An attribute for checkbox/radio form field layout
*
Expand Down Expand Up @@ -462,6 +466,18 @@ class CRM_Report_Form extends CRM_Core_Form {

protected $sql;

/**
* An instruction not to add a Group By.
*
* This is relevant where the group by might be otherwise added after the code that determines the group by array.
*
* e.g. where stat fields are being added but other settings cause it to not be desirable to add a group by
* such as in pivot charts when no row header is set
*
* @var bool
*/
protected $noGroupBy = FALSE;

/**
* SQL being run in this report as an array.
*
Expand Down Expand Up @@ -2319,7 +2335,14 @@ public function select() {
$select = $this->addStatisticsToSelect($field, $tableName, $fieldName, $select);
}
else {
$select = $this->addBasicFieldToSelect($tableName, $fieldName, $field, $select);

$selectClause = $this->getSelectClauseWithGroupConcatIfNotGroupedBy($tableName, $fieldName, $field);
if ($selectClause) {
$select[] = $selectClause;
}
else {
$select = $this->addBasicFieldToSelect($tableName, $fieldName, $field, $select);
}
}
}
}
Expand Down Expand Up @@ -2420,6 +2443,14 @@ public function select() {
* @return bool
*/
public function selectClause(&$tableName, $tableKey, &$fieldName, &$field) {
if (!empty($field['pseudofield'])) {
$alias = "{$tableName}_{$fieldName}";
$this->_columnHeaders["{$tableName}_{$fieldName}"]['title'] = CRM_Utils_Array::value('title', $field);
$this->_columnHeaders["{$tableName}_{$fieldName}"]['type'] = CRM_Utils_Array::value('type', $field);
$this->_columnHeaders["{$tableName}_{$fieldName}"]['dbAlias'] = CRM_Utils_Array::value('dbAlias', $field);
$this->_selectAliases[] = $alias;
return ' 1 as ' . $alias;
}
return FALSE;
}

Expand Down Expand Up @@ -2629,10 +2660,12 @@ public function buildQuery($applyLimit = TRUE) {
$this->groupBy();
$this->orderBy();

// order_by columns not selected for display need to be included in SELECT
$unselectedSectionColumns = $this->unselectedSectionColumns();
foreach ($unselectedSectionColumns as $alias => $section) {
$this->_select .= ", {$section['dbAlias']} as {$alias}";
foreach ($this->unselectedOrderByColumns() as $alias => $field) {
$clause = $this->getSelectClauseWithGroupConcatIfNotGroupedBy($field['table_name'], $field['name'], $field);
if (!$clause) {
$clause = "{$field['dbAlias']} as {$alias}";
}
$this->_select .= ", $clause ";
}

if ($applyLimit && empty($this->_params['charts'])) {
Expand Down Expand Up @@ -2712,7 +2745,16 @@ public function storeOrderByArray() {

if (!empty($orderByField)) {
$this->_orderByFields[$orderByField['tplField']] = $orderByField;
$orderBys[] = "{$orderByField['dbAlias']} {$orderBy['order']}";
if ($this->groupConcatTested) {
$orderBys[$orderByField['tplField']] = "{$orderByField['tplField']} {$orderBy['order']}";
}
else {
// Not sure when this is preferable to using tplField (which has
// definitely been tested to work in cases then this does not.
// in caution not switching unless report has been tested for
// group concat functionality.
$orderBys[$orderByField['tplField']] = "{$orderByField['dbAlias']} {$orderBy['order']}";
}

// Record any section headers for assignment to the template
if (!empty($orderBy['section'])) {
Expand All @@ -2728,6 +2770,15 @@ public function storeOrderByArray() {
$this->assign('sections', $this->_sections);
}

/**
* Determine unselected columns.
*
* @return array
*/
public function unselectedOrderByColumns() {
return array_diff_key($this->_orderByFields, $this->getSelectColumns());
}

/**
* Determine unselected columns.
*
Expand Down Expand Up @@ -5349,4 +5400,31 @@ protected function getDefaultsFromOptions($options) {
return $defaults;
}

/**
* Get the select clause for a field, wrapping in GROUP_CONCAT if appropriate.
*
* Full group by mode dictates that a field must either be in the group by function or
* wrapped in a aggregate function. Here we wrap the field in GROUP_CONCAT if it is not in the
* group concat.
*
* @param string $tableName
* @param string $fieldName
* @param string $field
* @return string
*/
protected function getSelectClauseWithGroupConcatIfNotGroupedBy($tableName, &$fieldName, &$field) {
if ($this->groupConcatTested && (!empty($this->_groupByArray) || $this->isForceGroupBy)) {
if ((empty($field['statistics']) || in_array('GROUP_CONCAT', $field['statistics']))) {
$label = CRM_Utils_Array::value('title', $field);
$alias = "{$tableName}_{$fieldName}";
$this->_columnHeaders["{$tableName}_{$fieldName}"]['title'] = $label;
$this->_selectAliases[] = $alias;
if (empty($this->_groupByArray[$tableName . '_' . $fieldName])) {
return "GROUP_CONCAT(DISTINCT {$field['dbAlias']}) as $alias";
}
return "({$field['dbAlias']}) as $alias";
}
}
}

}
66 changes: 10 additions & 56 deletions CRM/Report/Form/Contribute/Detail.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ class CRM_Report_Form_Contribute_Detail extends CRM_Report_Form {
'Contribution',
);

protected $groupConcatTested = TRUE;

/**
* This report has been optimised for group filtering.
*
Expand Down Expand Up @@ -221,6 +223,13 @@ public function __construct() {
'payment_instrument_id' => array('title' => ts('Payment Method')),
'receive_date' => array('title' => ts('Date Received')),
),
'group_bys' => array(
'contribution_id' => array(
'name' => 'id',
'required' => TRUE,
'title' => ts('Contribution'),
),
),
'grouping' => 'contri-fields',
),
'civicrm_contribution_soft' => array(
Expand All @@ -243,7 +252,6 @@ public function __construct() {
'fields' => array(
'card_type_id' => array(
'title' => ts('Credit Card Type'),
'dbAlias' => 'GROUP_CONCAT(financial_trxn_civireport.card_type_id SEPARATOR ",")',
),
),
'filters' => array(
Expand Down Expand Up @@ -332,30 +340,6 @@ public function __construct() {
parent::__construct();
}

public function preProcess() {
parent::preProcess();
}

public function select() {
$this->_columnHeaders = array();

parent::select();
}

public function orderBy() {
parent::orderBy();

// please note this will just add the order-by columns to select query, and not display in column-headers.
// This is a solution to not throw fatal errors when there is a column in order-by, not present in select/display columns.
foreach ($this->_orderByFields as $orderBy) {
if (!array_key_exists($orderBy['name'], $this->_params['fields']) &&
empty($orderBy['section']) && (strpos($this->_select, $orderBy['dbAlias']) === FALSE)
) {
$this->_select .= ", {$orderBy['dbAlias']} as {$orderBy['tplField']}";
}
}
}

/**
* Set the FROM clause for the report.
*/
Expand All @@ -381,11 +365,6 @@ public function from() {
$this->appendAdditionalFromJoins();
}

public function groupBy() {
$groupBy = array("{$this->_aliases['civicrm_contact']}.id", "{$this->_aliases['civicrm_contribution']}.id");
$this->_groupBy = CRM_Contact_BAO_Query::getGroupByFromSelectColumns($this->_selectClauses, $groupBy);
}

/**
* @param $rows
*
Expand Down Expand Up @@ -584,34 +563,9 @@ public function postProcess() {
$this->addToDeveloperTab($sql);
CRM_Core_DAO::executeQuery($sql);

// 5. Re-construct order-by to make sense for final query on temp3 table
$orderBy = '';
if (!empty($this->_orderByArray)) {
$aliases = array_flip($this->_aliases);
$orderClause = array();
foreach ($this->_orderByArray as $clause) {
list($alias, $rest) = explode('.', $clause);
// CRM-17280 -- In case, we are ordering by custom fields
// modify $rest to match the alias used for them in temp3 table
$grp = new CRM_Core_DAO_CustomGroup();
$grp->table_name = $aliases[$alias];
if ($grp->find()) {
list($fld, $order) = explode(' ', $rest);
foreach ($this->_columns[$aliases[$alias]]['fields'] as $fldName => $value) {
if ($value['name'] == $fld) {
$fld = $fldName;
}
}
$rest = "{$fld} {$order}";
}
$orderClause[] = $aliases[$alias] . "_" . $rest;
}
$orderBy = (!empty($orderClause)) ? "ORDER BY " . implode(', ', $orderClause) : '';
}

// 6. show result set from temp table 3
$rows = array();
$sql = "SELECT * FROM civireport_contribution_detail_temp3 {$orderBy}";
$sql = "SELECT * FROM civireport_contribution_detail_temp3 $this->_orderBy";
$this->buildRows($sql, $rows);

// format result set.
Expand Down

0 comments on commit ee56e91

Please sign in to comment.