protected $groupFilterNotOptimised = FALSE;
/**
- * Indicate that report is not fully FGB compliant.
+ * Use the generic (but flawed) handling to implement full group by.
+ *
+ * Note that because we are calling the parent group by function we set this to FALSE.
+ * The parent group by function adds things to the group by in order to make the mysql pass
+ * but can create incorrect results in the process.
*
* @var bool
*/
- public $optimisedForOnlyFullGroupBy;
+ public $optimisedForOnlyFullGroupBy = FALSE;
/**
* Class constructor.
* Set group by clause.
*/
public function groupBy() {
- $this->_groupBy = "";
- $groupByColumns = [];
- $append = FALSE;
+ parent::groupBy();
+
+ $isGroupByFrequency = !empty($this->_params['group_bys_freq']);
+
if (!empty($this->_params['group_bys']) &&
is_array($this->_params['group_bys'])
) {
- foreach ($this->_columns as $tableName => $table) {
- if (array_key_exists('group_bys', $table)) {
- foreach ($table['group_bys'] as $fieldName => $field) {
- if (!empty($this->_params['group_bys'][$fieldName])) {
- if (!empty($field['chart'])) {
- $this->assign('chartSupported', TRUE);
- }
-
- if (!empty($table['group_bys'][$fieldName]['frequency']) &&
- !empty($this->_params['group_bys_freq'][$fieldName])
- ) {
-
- $append = "YEAR({$field['dbAlias']});;";
- if (in_array(strtolower($this->_params['group_bys_freq'][$fieldName]),
- ['year']
- )) {
- $append = '';
- }
- if ($this->_params['group_bys_freq'][$fieldName] == 'FISCALYEAR') {
- $groupByColumns[] = self::fiscalYearOffset($field['dbAlias']);
- }
- else {
- $groupByColumns[] = "$append {$this->_params['group_bys_freq'][$fieldName]}({$field['dbAlias']})";
- }
- $append = TRUE;
- }
- else {
- $groupByColumns[] = $field['dbAlias'];
- }
- }
- }
- }
- }
if (!empty($this->_statFields) &&
- (($append && count($groupByColumns) <= 1) || (!$append)) &&
+ (($isGroupByFrequency && count($this->_groupByArray) <= 1) || (!$isGroupByFrequency)) &&
!$this->_having
) {
$this->_rollup = " WITH ROLLUP";
}
$groupBy = [];
- foreach ($groupByColumns as $key => $val) {
+ foreach ($this->_groupByArray as $key => $val) {
if (strpos($val, ';;') !== FALSE) {
$groupBy = array_merge($groupBy, explode(';;', $val));
}
else {
- $groupBy[] = $groupByColumns[$key];
+ $groupBy[] = $this->_groupByArray[$key];
}
}
$this->_groupBy = "GROUP BY " . implode(', ', $groupBy);
* @param array $rows
*
* @return array
+ *
+ * @throws \CRM_Core_Exception
*/
public function statistics(&$rows) {
$statistics = parent::statistics($rows);
$softCredit = CRM_Utils_Array::value('soft_amount', $this->_params['fields']);
$onlySoftCredit = $softCredit && !CRM_Utils_Array::value('total_amount', $this->_params['fields']);
- if (empty($this->_groupBy)) {
- $group = "\nGROUP BY {$this->_aliases['civicrm_contribution']}.currency";
- }
- else {
- $group = "\n {$this->_groupBy}, {$this->_aliases['civicrm_contribution']}.currency";
+ if (!isset($this->_groupByArray['civicrm_contribution_currency'])) {
+ $this->_groupByArray['civicrm_contribution_currency'] = 'currency';
}
+ $group = ' GROUP BY ' . implode(', ', $this->_groupByArray);
$this->from('contribution');
if ($softCredit) {
/**
* Test the group filter works on the contribution summary when 2 groups are involved.
+ *
+ * @throws \CRM_Core_Exception
*/
public function testContributionSummaryWithTwoGroups() {
$groupID = $this->setUpPopulatedGroup();
$this->assertEquals(4, $rows['values'][0]['civicrm_contribution_total_amount_count']);
}
+ /**
+ * Test we don't get a fatal grouping by only contribution status id.
+ *
+ * @throws \CRM_Core_Exception
+ */
+ public function testContributionSummaryGroupByContributionStatus() {
+ $params = [
+ 'report_id' => 'contribute/summary',
+ 'fields' => ['total_amount' => 1, 'country_id' => 1],
+ 'group_bys' => ['contribution_status_id' => 1],
+ 'options' => ['metadata' => ['sql']],
+ ];
+ $rowsSql = $this->callAPISuccess('report_template', 'getrows', $params)['metadata']['sql'];
+ $this->assertContains('GROUP BY contribution_civireport.contribution_status_id WITH ROLLUP', $rowsSql[0]);
+ $statsSql = $this->callAPISuccess('report_template', 'getstatistics', $params)['metadata']['sql'];
+ $this->assertContains('GROUP BY contribution_civireport.contribution_status_id, currency', $statsSql[2]);
+ }
+
+ /**
+ * Test we don't get a fatal grouping by only contribution status id.
+ *
+ * @throws \CRM_Core_Exception
+ */
+ public function testContributionSummaryGroupByYearFrequency() {
+ $params = [
+ 'report_id' => 'contribute/summary',
+ 'fields' => ['total_amount' => 1, 'country_id' => 1],
+ 'group_bys' => ['receive_date' => 1],
+ 'group_bys_freq' => ['receive_date' => 'YEAR'],
+ 'options' => ['metadata' => ['sql']],
+ ];
+ $rowsSql = $this->callAPISuccess('report_template', 'getrows', $params)['metadata']['sql'];
+ $this->assertContains('GROUP BY YEAR(contribution_civireport.receive_date) WITH ROLLUP', $rowsSql[0]);
+ $statsSql = $this->callAPISuccess('report_template', 'getstatistics', $params)['metadata']['sql'];
+ $this->assertContains('GROUP BY YEAR(contribution_civireport.receive_date), currency', $statsSql[2]);
+ }
+
+ /**
+ * Test we don't get a fatal grouping with QUARTER frequency.
+ *
+ * @throws \CRM_Core_Exception
+ */
+ public function testContributionSummaryGroupByYearQuarterFrequency() {
+ $params = [
+ 'report_id' => 'contribute/summary',
+ 'fields' => ['total_amount' => 1, 'country_id' => 1],
+ 'group_bys' => ['receive_date' => 1],
+ 'group_bys_freq' => ['receive_date' => 'QUARTER'],
+ 'options' => ['metadata' => ['sql']],
+ ];
+ $rowsSql = $this->callAPISuccess('report_template', 'getrows', $params)['metadata']['sql'];
+ $this->assertContains('GROUP BY YEAR(contribution_civireport.receive_date), QUARTER(contribution_civireport.receive_date) WITH ROLLUP', $rowsSql[0]);
+ $statsSql = $this->callAPISuccess('report_template', 'getstatistics', $params)['metadata']['sql'];
+ $this->assertContains('GROUP BY YEAR(contribution_civireport.receive_date), QUARTER(contribution_civireport.receive_date), currency', $statsSql[2]);
+ }
+
+ /**
+ * Test we don't get a fatal grouping with QUARTER frequency.
+ *
+ * @throws \CRM_Core_Exception
+ */
+ public function testContributionSummaryGroupByDateFrequency() {
+ $params = [
+ 'report_id' => 'contribute/summary',
+ 'fields' => ['total_amount' => 1, 'country_id' => 1],
+ 'group_bys' => ['receive_date' => 1],
+ 'group_bys_freq' => ['receive_date' => 'DATE'],
+ 'options' => ['metadata' => ['sql']],
+ ];
+ $rowsSql = $this->callAPISuccess('report_template', 'getrows', $params)['metadata']['sql'];
+ $this->assertContains('GROUP BY DATE(contribution_civireport.receive_date) WITH ROLLUP', $rowsSql[0]);
+ $statsSql = $this->callAPISuccess('report_template', 'getstatistics', $params)['metadata']['sql'];
+ $this->assertContains('GROUP BY DATE(contribution_civireport.receive_date), currency', $statsSql[2]);
+ }
+
+ /**
+ * Test we don't get a fatal grouping with QUARTER frequency.
+ *
+ * @throws \CRM_Core_Exception
+ */
+ public function testContributionSummaryGroupByWeekFrequency() {
+ $params = [
+ 'report_id' => 'contribute/summary',
+ 'fields' => ['total_amount' => 1, 'country_id' => 1],
+ 'group_bys' => ['receive_date' => 1],
+ 'group_bys_freq' => ['receive_date' => 'YEARWEEK'],
+ 'options' => ['metadata' => ['sql']],
+ ];
+ $rowsSql = $this->callAPISuccess('report_template', 'getrows', $params)['metadata']['sql'];
+ $this->assertContains('GROUP BY YEARWEEK(contribution_civireport.receive_date) WITH ROLLUP', $rowsSql[0]);
+ $statsSql = $this->callAPISuccess('report_template', 'getstatistics', $params)['metadata']['sql'];
+ $this->assertContains('GROUP BY YEARWEEK(contribution_civireport.receive_date), currency', $statsSql[2]);
+ }
+
/**
* CRM-20640: Test the group filter works on the contribution summary when a single contact in 2 groups.
+ *
+ * @throws \CRM_Core_Exception
*/
public function testContributionSummaryWithSingleContactsInTwoGroups() {
list($groupID1, $individualID) = $this->setUpPopulatedGroup(TRUE);
* whenever they are hard-added or in the criteria.
*
* @return int
+ * @throws \CRM_Core_Exception
*/
public function setUpPopulatedSmartGroup() {
$household1ID = $this->householdCreate();
* @param bool $returnAddedContact
*
* @return int
+ * @throws \CRM_Core_Exception
*/
public function setUpPopulatedGroup($returnAddedContact = FALSE) {
$individual1ID = $this->individualCreate();
/**
* @return array
+ *
+ * @throws \CRM_Core_Exception
*/
public function setUpIntersectingGroups() {
$groupID = $this->setUpPopulatedGroup();
*
* @param string $template
* Report template unique identifier.
+ *
+ * @throws \CRM_Core_Exception
*/
public function testReportsWithNoTInSmartGroupFilter($template) {
$groupID = $this->setUpPopulatedGroup();