3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
15 * @copyright CiviCRM LLC https://civicrm.org/licensing
17 class CRM_Report_Form_Contribute_Summary
extends CRM_Report_Form
{
19 protected $_charts = [
21 'barChart' => 'Bar Chart',
22 'pieChart' => 'Pie Chart',
24 protected $_customGroupExtends = ['Contribution', 'Contact', 'Individual'];
25 protected $_customGroupGroupBy = TRUE;
27 public $_drilldownReport = ['contribute/detail' => 'Link to Detail Report'];
30 * To what frequency group-by a date column
34 protected $_groupByDateFreq = [
38 'QUARTER' => 'Quarter',
40 'FISCALYEAR' => 'Fiscal Year',
44 * This report has been optimised for group filtering.
50 protected $groupFilterNotOptimised = FALSE;
53 * Indicate that report is not fully FGB compliant.
57 public $optimisedForOnlyFullGroupBy;
62 public function __construct() {
64 'civicrm_contact' => [
65 'dao' => 'CRM_Contact_DAO_Contact',
66 'fields' => array_merge(
67 $this->getBasicContactFields(),
70 'title' => ts('Contact Name'),
75 'filters' => $this->getBasicContactFilters(['deceased' => NULL]),
76 'grouping' => 'contact-fields',
78 'id' => ['title' => ts('Contact ID')],
80 'title' => ts('Contact Name'),
85 'dao' => 'CRM_Core_DAO_Email',
88 'title' => ts('Email'),
92 'grouping' => 'contact-fields',
94 'civicrm_line_item' => [
95 'dao' => 'CRM_Price_DAO_LineItem',
98 'dao' => 'CRM_Core_DAO_Phone',
101 'title' => ts('Phone'),
105 'grouping' => 'contact-fields',
107 'civicrm_financial_type' => [
108 'dao' => 'CRM_Financial_DAO_FinancialType',
109 'fields' => ['financial_type' => NULL],
110 'grouping' => 'contri-fields',
112 'financial_type' => ['title' => ts('Financial Type')],
115 'civicrm_contribution' => [
116 'dao' => 'CRM_Contribute_DAO_Contribution',
117 //'bao' => 'CRM_Contribute_BAO_Contribution',
119 'contribution_status_id' => [
120 'title' => ts('Contribution Status'),
122 'contribution_source' => ['title' => ts('Source')],
125 'no_display' => TRUE,
127 'contribution_page_id' => [
128 'title' => ts('Contribution Page'),
131 'title' => ts('Contribution Amount Stats'),
134 'count' => ts('Contributions'),
135 'sum' => ts('Contribution Aggregate'),
136 'avg' => ts('Contribution Avg'),
139 'non_deductible_amount' => [
140 'title' => ts('Non-deductible Amount'),
143 'grouping' => 'contri-fields',
145 'receive_date' => ['operatorType' => CRM_Report_Form
::OP_DATE
],
146 'thankyou_date' => ['operatorType' => CRM_Report_Form
::OP_DATE
],
147 'contribution_status_id' => [
148 'title' => ts('Contribution Status'),
149 'operatorType' => CRM_Report_Form
::OP_MULTISELECT
,
150 'options' => CRM_Contribute_BAO_Contribution
::buildOptions('contribution_status_id', 'search'),
152 'type' => CRM_Utils_Type
::T_INT
,
154 'contribution_page_id' => [
155 'title' => ts('Contribution Page'),
156 'operatorType' => CRM_Report_Form
::OP_MULTISELECT
,
157 'options' => CRM_Contribute_PseudoConstant
::contributionPage(),
158 'type' => CRM_Utils_Type
::T_INT
,
161 'title' => ts('Currency'),
162 'operatorType' => CRM_Report_Form
::OP_MULTISELECT
,
163 'options' => CRM_Core_OptionGroup
::values('currencies_enabled'),
165 'type' => CRM_Utils_Type
::T_STRING
,
167 'financial_type_id' => [
168 'title' => ts('Financial Type'),
169 'operatorType' => CRM_Report_Form
::OP_MULTISELECT
,
170 'options' => CRM_Financial_BAO_FinancialType
::getAvailableFinancialTypes(),
171 'type' => CRM_Utils_Type
::T_INT
,
173 'contribution_page_id' => [
174 'title' => ts('Contribution Page'),
175 'operatorType' => CRM_Report_Form
::OP_MULTISELECT
,
176 'options' => CRM_Contribute_PseudoConstant
::contributionPage(),
177 'type' => CRM_Utils_Type
::T_INT
,
180 'title' => ts('Contribution Amount'),
182 'non_deductible_amount' => [
183 'title' => ts('Non-deductible Amount'),
186 'title' => ts('Contribution Aggregate'),
187 'type' => CRM_Report_Form
::OP_INT
,
188 'dbAlias' => 'civicrm_contribution_total_amount_sum',
192 'title' => ts('Contribution Count'),
193 'type' => CRM_Report_Form
::OP_INT
,
194 'dbAlias' => 'civicrm_contribution_total_amount_count',
198 'title' => ts('Contribution Avg'),
199 'type' => CRM_Report_Form
::OP_INT
,
200 'dbAlias' => 'civicrm_contribution_total_amount_avg',
210 'contribution_source' => NULL,
211 'contribution_status_id' => [
212 'title' => ts('Contribution Status'),
213 'operatorType' => CRM_Report_Form
::OP_MULTISELECT
,
214 'options' => CRM_Contribute_BAO_Contribution
::buildOptions('contribution_status_id', 'search'),
216 'type' => CRM_Utils_Type
::T_INT
,
218 'contribution_page_id' => [
219 'title' => ts('Contribution Page'),
220 'operatorType' => CRM_Report_Form
::OP_MULTISELECT
,
221 'options' => CRM_Contribute_PseudoConstant
::contributionPage(),
222 'type' => CRM_Utils_Type
::T_INT
,
226 'civicrm_financial_trxn' => [
227 'dao' => 'CRM_Financial_DAO_FinancialTrxn',
230 'title' => ts('Credit Card Type'),
231 'dbAlias' => 'GROUP_CONCAT(financial_trxn_civireport.card_type_id SEPARATOR ",")',
236 'title' => ts('Credit Card Type'),
237 'operatorType' => CRM_Report_Form
::OP_MULTISELECT
,
238 'options' => CRM_Financial_DAO_FinancialTrxn
::buildOptions('card_type_id'),
240 'type' => CRM_Utils_Type
::T_STRING
,
245 'dao' => 'CRM_Batch_DAO_EntityBatch',
246 'grouping' => 'contri-fields',
249 'name' => 'batch_id',
250 'title' => ts('Batch Title'),
251 'dbAlias' => 'GROUP_CONCAT(DISTINCT batch_civireport.batch_id
252 ORDER BY batch_civireport.batch_id SEPARATOR ",")',
257 'title' => ts('Batch Title'),
258 'operatorType' => CRM_Report_Form
::OP_MULTISELECT
,
259 'options' => CRM_Batch_BAO_Batch
::getBatches(),
260 'type' => CRM_Utils_Type
::T_INT
,
264 'batch_id' => ['title' => ts('Batch Title')],
267 'civicrm_contribution_soft' => [
268 'dao' => 'CRM_Contribute_DAO_ContributionSoft',
271 'title' => ts('Soft Credit Amount Stats'),
274 'count' => ts('Soft Credits'),
275 'sum' => ts('Soft Credit Aggregate'),
276 'avg' => ts('Soft Credit Avg'),
280 'grouping' => 'contri-fields',
283 'title' => ts('Soft Credit Amount'),
285 'soft_credit_type_id' => [
286 'title' => ts('Soft Credit Type'),
287 'operatorType' => CRM_Report_Form
::OP_MULTISELECT
,
288 'options' => CRM_Core_OptionGroup
::values('soft_credit_type'),
290 'type' => CRM_Utils_Type
::T_STRING
,
293 'title' => ts('Soft Credit Aggregate'),
294 'type' => CRM_Report_Form
::OP_INT
,
295 'dbAlias' => 'civicrm_contribution_soft_soft_amount_sum',
299 'title' => ts('Soft Credits Count'),
300 'type' => CRM_Report_Form
::OP_INT
,
301 'dbAlias' => 'civicrm_contribution_soft_soft_amount_count',
305 'title' => ts('Soft Credit Avg'),
306 'type' => CRM_Report_Form
::OP_INT
,
307 'dbAlias' => 'civicrm_contribution_soft_soft_amount_avg',
312 ] +
$this->addAddressFields();
314 $this->addCampaignFields('civicrm_contribution', TRUE);
316 $this->_tagFilter
= TRUE;
317 $this->_groupFilter
= TRUE;
318 $this->_currencyColumn
= 'civicrm_contribution_currency';
319 parent
::__construct();
325 public function select() {
327 $this->_columnHeaders
= [];
328 foreach ($this->_columns
as $tableName => $table) {
329 if (array_key_exists('group_bys', $table)) {
330 foreach ($table['group_bys'] as $fieldName => $field) {
331 if (!empty($this->_params
['group_bys'][$fieldName])) {
332 switch (CRM_Utils_Array
::value($fieldName, $this->_params
['group_bys_freq'])) {
334 $select[] = "DATE_SUB({$field['dbAlias']}, INTERVAL WEEKDAY({$field['dbAlias']}) DAY) AS {$tableName}_{$fieldName}_start";
335 $select[] = "YEARWEEK({$field['dbAlias']}) AS {$tableName}_{$fieldName}_subtotal";
336 $select[] = "WEEKOFYEAR({$field['dbAlias']}) AS {$tableName}_{$fieldName}_interval";
337 $field['title'] = ts('Week Beginning');
341 $select[] = "MAKEDATE(YEAR({$field['dbAlias']}), 1) AS {$tableName}_{$fieldName}_start";
342 $select[] = "YEAR({$field['dbAlias']}) AS {$tableName}_{$fieldName}_subtotal";
343 $select[] = "YEAR({$field['dbAlias']}) AS {$tableName}_{$fieldName}_interval";
344 $field['title'] = ts('Year Beginning');
348 $config = CRM_Core_Config
::singleton();
349 $fy = $config->fiscalYearStart
;
350 $fiscal = self
::fiscalYearOffset($field['dbAlias']);
352 $select[] = "DATE_ADD(MAKEDATE({$fiscal}, 1), INTERVAL ({$fy{'M'}})-1 MONTH) AS {$tableName}_{$fieldName}_start";
353 $select[] = "{$fiscal} AS {$tableName}_{$fieldName}_subtotal";
354 $select[] = "{$fiscal} AS {$tableName}_{$fieldName}_interval";
355 $field['title'] = ts('Fiscal Year Beginning');
359 $select[] = "DATE_SUB({$field['dbAlias']}, INTERVAL (DAYOFMONTH({$field['dbAlias']})-1) DAY) as {$tableName}_{$fieldName}_start";
360 $select[] = "MONTH({$field['dbAlias']}) AS {$tableName}_{$fieldName}_subtotal";
361 $select[] = "MONTHNAME({$field['dbAlias']}) AS {$tableName}_{$fieldName}_interval";
362 $field['title'] = ts('Month Beginning');
366 $select[] = "DATE({$field['dbAlias']}) as {$tableName}_{$fieldName}_start";
367 $field['title'] = ts('Date');
371 $select[] = "STR_TO_DATE(CONCAT( 3 * QUARTER( {$field['dbAlias']} ) -2 , '/', '1', '/', YEAR( {$field['dbAlias']} ) ), '%m/%d/%Y') AS {$tableName}_{$fieldName}_start";
372 $select[] = "QUARTER({$field['dbAlias']}) AS {$tableName}_{$fieldName}_subtotal";
373 $select[] = "QUARTER({$field['dbAlias']}) AS {$tableName}_{$fieldName}_interval";
374 $field['title'] = 'Quarter';
377 if (!empty($this->_params
['group_bys_freq'][$fieldName])) {
378 $this->_interval
= $this->_params
['group_bys_freq'][$fieldName];
379 $this->_columnHeaders
["{$tableName}_{$fieldName}_start"]['title'] = $field['title'];
380 $this->_columnHeaders
["{$tableName}_{$fieldName}_start"]['type'] = $field['type'];
381 $this->_columnHeaders
["{$tableName}_{$fieldName}_start"]['group_by'] = $this->_params
['group_bys_freq'][$fieldName];
383 // just to make sure these values are transferred to rows.
384 // since we need that for calculation purpose,
385 // e.g making subtotals look nicer or graphs
386 $this->_columnHeaders
["{$tableName}_{$fieldName}_interval"] = ['no_display' => TRUE];
387 $this->_columnHeaders
["{$tableName}_{$fieldName}_subtotal"] = ['no_display' => TRUE];
393 if (array_key_exists('fields', $table)) {
394 foreach ($table['fields'] as $fieldName => $field) {
395 if (!empty($field['required']) ||
396 !empty($this->_params
['fields'][$fieldName])
398 // only include statistics columns if set
399 if (!empty($field['statistics'])) {
400 foreach ($field['statistics'] as $stat => $label) {
401 $this->_columnHeaders
["{$tableName}_{$fieldName}_{$stat}"]['title'] = $label;
402 $this->_columnHeaders
["{$tableName}_{$fieldName}_{$stat}"]['type'] = $field['type'];
403 $this->_statFields
[] = "{$tableName}_{$fieldName}_{$stat}";
404 switch (strtolower($stat)) {
406 $select[] = "SUM({$field['dbAlias']}) as {$tableName}_{$fieldName}_{$stat}";
410 $select[] = "COUNT({$field['dbAlias']}) as {$tableName}_{$fieldName}_{$stat}";
411 $this->_columnHeaders
["{$tableName}_{$fieldName}_{$stat}"]['type'] = CRM_Utils_Type
::T_INT
;
415 $select[] = "ROUND(AVG({$field['dbAlias']}),2) as {$tableName}_{$fieldName}_{$stat}";
421 $select[] = "{$field['dbAlias']} as {$tableName}_{$fieldName}";
422 $this->_columnHeaders
["{$tableName}_{$fieldName}"]['type'] = CRM_Utils_Array
::value('type', $field);
423 $this->_columnHeaders
["{$tableName}_{$fieldName}"]['title'] = CRM_Utils_Array
::value('title', $field);
430 $this->_selectClauses
= $select;
431 $this->_select
= "SELECT " . implode(', ', $select) . " ";
437 * @param array $fields
438 * @param array $files
439 * @param CRM_Report_Form_Contribute_Summary $self
443 public static function formRule($fields, $files, $self) {
444 // Check for searching combination of display columns and
446 $ignoreFields = ['total_amount', 'sort_name'];
447 $errors = $self->customDataFormRule($fields, $ignoreFields);
449 if (empty($fields['fields']['total_amount'])) {
455 if (!empty($fields[$val])) {
456 $errors[$val] = ts("Please select the Amount Statistics");
467 * @param string $entity
469 * @todo fix function signature to match parent. Remove hacky passing of $entity
470 * to acheive unclear results.
472 public function from($entity = NULL) {
473 $softCreditJoinType = "LEFT";
474 if (!empty($this->_params
['fields']['soft_amount']) &&
475 empty($this->_params
['fields']['total_amount'])
477 // if its only soft credit stats, use inner join
478 $softCreditJoinType = "INNER";
481 $softCreditJoin = "{$softCreditJoinType} JOIN civicrm_contribution_soft {$this->_aliases['civicrm_contribution_soft']}
482 ON {$this->_aliases['civicrm_contribution_soft']}.contribution_id = {$this->_aliases['civicrm_contribution']}.id";
483 if ($entity == 'contribution' ||
empty($this->_params
['fields']['soft_amount'])) {
484 $softCreditJoin .= " AND {$this->_aliases['civicrm_contribution_soft']}.id = (SELECT MIN(id) FROM civicrm_contribution_soft cs WHERE cs.contribution_id = {$this->_aliases['civicrm_contribution']}.id) ";
487 $this->setFromBase('civicrm_contact');
490 INNER JOIN civicrm_contribution {$this->_aliases['civicrm_contribution']}
491 ON {$this->_aliases['civicrm_contact']}.id = {$this->_aliases['civicrm_contribution']}.contact_id AND
492 {$this->_aliases['civicrm_contribution']}.is_test = 0
494 LEFT JOIN civicrm_financial_type {$this->_aliases['civicrm_financial_type']}
495 ON {$this->_aliases['civicrm_contribution']}.financial_type_id ={$this->_aliases['civicrm_financial_type']}.id
498 $this->joinAddressFromContact();
499 $this->joinPhoneFromContact();
500 $this->joinEmailFromContact();
502 //for contribution batches
503 if ($this->isTableSelected('civicrm_batch')) {
505 LEFT JOIN civicrm_entity_financial_trxn eft
506 ON eft.entity_id = {$this->_aliases['civicrm_contribution']}.id AND
507 eft.entity_table = 'civicrm_contribution'
508 LEFT JOIN civicrm_entity_batch {$this->_aliases['civicrm_batch']}
509 ON ({$this->_aliases['civicrm_batch']}.entity_id = eft.financial_trxn_id
510 AND {$this->_aliases['civicrm_batch']}.entity_table = 'civicrm_financial_trxn')";
513 $this->addFinancialTrxnFromClause();
517 * Set group by clause.
519 public function groupBy() {
520 $this->_groupBy
= "";
521 $groupByColumns = [];
523 if (!empty($this->_params
['group_bys']) &&
524 is_array($this->_params
['group_bys'])
526 foreach ($this->_columns
as $tableName => $table) {
527 if (array_key_exists('group_bys', $table)) {
528 foreach ($table['group_bys'] as $fieldName => $field) {
529 if (!empty($this->_params
['group_bys'][$fieldName])) {
530 if (!empty($field['chart'])) {
531 $this->assign('chartSupported', TRUE);
534 if (!empty($table['group_bys'][$fieldName]['frequency']) &&
535 !empty($this->_params
['group_bys_freq'][$fieldName])
538 $append = "YEAR({$field['dbAlias']});;";
539 if (in_array(strtolower($this->_params
['group_bys_freq'][$fieldName]),
544 if ($this->_params
['group_bys_freq'][$fieldName] == 'FISCALYEAR') {
545 $groupByColumns[] = self
::fiscalYearOffset($field['dbAlias']);
548 $groupByColumns[] = "$append {$this->_params['group_bys_freq'][$fieldName]}({$field['dbAlias']})";
553 $groupByColumns[] = $field['dbAlias'];
560 if (!empty($this->_statFields
) &&
561 (($append && count($groupByColumns) <= 1) ||
(!$append)) &&
564 $this->_rollup
= " WITH ROLLUP";
567 foreach ($groupByColumns as $key => $val) {
568 if (strpos($val, ';;') !== FALSE) {
569 $groupBy = array_merge($groupBy, explode(';;', $val));
572 $groupBy[] = $groupByColumns[$key];
575 $this->_groupBy
= "GROUP BY " . implode(', ', $groupBy);
578 $this->_groupBy
= "GROUP BY {$this->_aliases['civicrm_contact']}.id";
580 $this->_groupBy
.= $this->_rollup
;
584 * Store having clauses as an array.
586 public function storeWhereHavingClauseArray() {
587 parent
::storeWhereHavingClauseArray();
588 if (empty($this->_params
['fields']['soft_amount']) &&
589 !empty($this->_havingClauses
)
591 foreach ($this->_havingClauses
as $key => $havingClause) {
592 if (stristr($havingClause, 'soft_soft')) {
593 unset($this->_havingClauses
[$key]);
606 public function statistics(&$rows) {
607 $statistics = parent
::statistics($rows);
609 $softCredit = CRM_Utils_Array
::value('soft_amount', $this->_params
['fields']);
610 $onlySoftCredit = $softCredit && !CRM_Utils_Array
::value('total_amount', $this->_params
['fields']);
611 if (empty($this->_groupBy
)) {
612 $group = "\nGROUP BY {$this->_aliases['civicrm_contribution']}.currency";
615 $group = "\n {$this->_groupBy}, {$this->_aliases['civicrm_contribution']}.currency";
618 $this->from('contribution');
622 $this->customDataFrom();
624 // Ensure that Extensions that modify the from statement in the sql also modify it in the statistics.
625 CRM_Utils_Hook
::alterReportVar('sql', $this, $this);
628 COUNT({$this->_aliases['civicrm_contribution']}.total_amount ) as civicrm_contribution_total_amount_count,
629 SUM({$this->_aliases['civicrm_contribution']}.total_amount ) as civicrm_contribution_total_amount_sum,
630 ROUND(AVG({$this->_aliases['civicrm_contribution']}.total_amount), 2) as civicrm_contribution_total_amount_avg,
631 {$this->_aliases['civicrm_contribution']}.currency as currency
632 {$this->_from} {$this->_where}";
636 COUNT({$this->_aliases['civicrm_contribution_soft']}.amount ) as civicrm_contribution_soft_soft_amount_count,
637 SUM({$this->_aliases['civicrm_contribution_soft']}.amount ) as civicrm_contribution_soft_soft_amount_sum,
638 ROUND(AVG({$this->_aliases['civicrm_contribution_soft']}.amount), 2) as civicrm_contribution_soft_soft_amount_avg";
639 $contriQuery = "{$select}, {$contriQuery}";
640 $softSQL = "SELECT {$select}, {$this->_aliases['civicrm_contribution']}.currency as currency
641 {$this->_from} {$this->_where} {$group} {$this->_having}";
644 $contriSQL = "SELECT {$contriQuery} {$group} {$this->_having}";
645 $contriDAO = CRM_Core_DAO
::executeQuery($contriSQL);
646 $this->addToDeveloperTab($contriSQL);
647 $currencies = $currAmount = $currAverage = $currCount = [];
648 $totalAmount = $average = $mode = $median = [];
649 $softTotalAmount = $softAverage = $averageCount = $averageSoftCount = [];
650 $softCount = $count = 0;
651 while ($contriDAO->fetch()) {
652 if (!isset($currAmount[$contriDAO->currency
])) {
653 $currAmount[$contriDAO->currency
] = 0;
655 if (!isset($currCount[$contriDAO->currency
])) {
656 $currCount[$contriDAO->currency
] = 0;
658 if (!isset($currAverage[$contriDAO->currency
])) {
659 $currAverage[$contriDAO->currency
] = 0;
661 if (!isset($averageCount[$contriDAO->currency
])) {
662 $averageCount[$contriDAO->currency
] = 0;
664 $currAmount[$contriDAO->currency
] +
= $contriDAO->civicrm_contribution_total_amount_sum
;
665 $currCount[$contriDAO->currency
] +
= $contriDAO->civicrm_contribution_total_amount_count
;
666 $currAverage[$contriDAO->currency
] +
= $contriDAO->civicrm_contribution_total_amount_avg
;
667 $averageCount[$contriDAO->currency
]++
;
668 $count +
= $contriDAO->civicrm_contribution_total_amount_count
;
670 if (!in_array($contriDAO->currency
, $currencies)) {
671 $currencies[] = $contriDAO->currency
;
675 foreach ($currencies as $currency) {
676 $totalAmount[] = CRM_Utils_Money
::format($currAmount[$currency], $currency) .
677 " (" . $currCount[$currency] . ")";
678 $average[] = CRM_Utils_Money
::format(($currAverage[$currency] / $averageCount[$currency]), $currency);
681 $groupBy = "\n{$group}, {$this->_aliases['civicrm_contribution']}.total_amount";
682 $orderBy = "\nORDER BY civicrm_contribution_total_amount_count DESC";
683 $modeSQL = "SELECT MAX(civicrm_contribution_total_amount_count) as civicrm_contribution_total_amount_count,
684 SUBSTRING_INDEX(GROUP_CONCAT(amount ORDER BY mode.civicrm_contribution_total_amount_count DESC SEPARATOR ';'), ';', 1) as amount,
686 FROM (SELECT {$this->_aliases['civicrm_contribution']}.total_amount as amount,
687 {$contriQuery} {$groupBy} {$orderBy}) as mode GROUP BY currency";
689 $mode = $this->calculateMode($modeSQL);
690 $median = $this->calculateMedian();
692 $currencies = $currSoftAmount = $currSoftAverage = $currSoftCount = [];
694 $softDAO = CRM_Core_DAO
::executeQuery($softSQL);
695 $this->addToDeveloperTab($softSQL);
696 while ($softDAO->fetch()) {
697 if (!isset($currSoftAmount[$softDAO->currency
])) {
698 $currSoftAmount[$softDAO->currency
] = 0;
700 if (!isset($currSoftCount[$softDAO->currency
])) {
701 $currSoftCount[$softDAO->currency
] = 0;
703 if (!isset($currSoftAverage[$softDAO->currency
])) {
704 $currSoftAverage[$softDAO->currency
] = 0;
706 if (!isset($averageSoftCount[$softDAO->currency
])) {
707 $averageSoftCount[$softDAO->currency
] = 0;
709 $currSoftAmount[$softDAO->currency
] +
= $softDAO->civicrm_contribution_soft_soft_amount_sum
;
710 $currSoftCount[$softDAO->currency
] +
= $softDAO->civicrm_contribution_soft_soft_amount_count
;
711 $currSoftAverage[$softDAO->currency
] +
= $softDAO->civicrm_contribution_soft_soft_amount_avg
;
712 $averageSoftCount[$softDAO->currency
]++
;
713 $softCount +
= $softDAO->civicrm_contribution_soft_soft_amount_count
;
715 if (!in_array($softDAO->currency
, $currencies)) {
716 $currencies[] = $softDAO->currency
;
720 foreach ($currencies as $currency) {
721 $softTotalAmount[] = CRM_Utils_Money
::format($currSoftAmount[$currency], $currency) .
722 " (" . $currSoftCount[$currency] . ")";
723 $softAverage[] = CRM_Utils_Money
::format(($currSoftAverage[$currency] / $averageSoftCount[$currency]), $currency);
727 if (!$onlySoftCredit) {
728 $statistics['counts']['amount'] = [
729 'title' => ts('Total Amount'),
730 'value' => implode(', ', $totalAmount),
731 'type' => CRM_Utils_Type
::T_STRING
,
733 $statistics['counts']['count'] = [
734 'title' => ts('Total Contributions'),
737 $statistics['counts']['avg'] = [
738 'title' => ts('Average'),
739 'value' => implode(', ', $average),
740 'type' => CRM_Utils_Type
::T_STRING
,
742 $statistics['counts']['mode'] = [
743 'title' => ts('Mode'),
744 'value' => implode(', ', $mode),
745 'type' => CRM_Utils_Type
::T_STRING
,
747 $statistics['counts']['median'] = [
748 'title' => ts('Median'),
749 'value' => implode(', ', $median),
750 'type' => CRM_Utils_Type
::T_STRING
,
754 $statistics['counts']['soft_amount'] = [
755 'title' => ts('Total Soft Credit Amount'),
756 'value' => implode(', ', $softTotalAmount),
757 'type' => CRM_Utils_Type
::T_STRING
,
759 $statistics['counts']['soft_count'] = [
760 'title' => ts('Total Soft Credits'),
761 'value' => $softCount,
763 $statistics['counts']['soft_avg'] = [
764 'title' => ts('Average Soft Credit'),
765 'value' => implode(', ', $softAverage),
766 'type' => CRM_Utils_Type
::T_STRING
,
773 * Post process function.
775 public function postProcess() {
776 $this->buildACLClause($this->_aliases
['civicrm_contact']);
777 parent
::postProcess();
785 public function buildChart(&$rows) {
788 if (!empty($this->_params
['charts'])) {
789 if (!empty($this->_params
['group_bys']['receive_date'])) {
791 $contrib = !empty($this->_params
['fields']['total_amount']) ?
TRUE : FALSE;
792 $softContrib = !empty($this->_params
['fields']['soft_amount']) ?
TRUE : FALSE;
794 foreach ($rows as $key => $row) {
795 if ($row['civicrm_contribution_receive_date_subtotal']) {
796 $graphRows['receive_date'][] = $row['civicrm_contribution_receive_date_start'];
797 $graphRows[$this->_interval
][] = $row['civicrm_contribution_receive_date_interval'];
798 if ($softContrib && $contrib) {
799 // both contri & soft contri stats are present
800 $graphRows['multiValue'][0][] = $row['civicrm_contribution_total_amount_sum'];
801 $graphRows['multiValue'][1][] = $row['civicrm_contribution_soft_soft_amount_sum'];
803 elseif ($softContrib) {
804 // only soft contributions
805 $graphRows['multiValue'][0][] = $row['civicrm_contribution_soft_soft_amount_sum'];
808 // only contributions
809 $graphRows['multiValue'][0][] = $row['civicrm_contribution_total_amount_sum'];
814 if ($softContrib && $contrib) {
815 $graphRows['barKeys'][0] = ts('Contributions');
816 $graphRows['barKeys'][1] = ts('Soft Credits');
817 $graphRows['legend'] = ts('Contributions and Soft Credits');
819 elseif ($softContrib) {
820 $graphRows['legend'] = ts('Soft Credits');
824 $config = CRM_Core_Config
::Singleton();
825 $graphRows['xname'] = $this->_interval
;
826 $graphRows['yname'] = ts('Amount (%1)', [1 => $config->defaultCurrency
]);
827 CRM_Utils_Chart
::chart($graphRows, $this->_params
['charts'], $this->_interval
);
828 $this->assign('chartType', $this->_params
['charts']);
834 * Alter display of rows.
836 * Iterate through the rows retrieved via SQL and make changes for display purposes,
837 * such as rendering contacts as links.
840 * Rows generated by SQL, with an array for each row.
842 public function alterDisplay(&$rows) {
844 $contributionStatus = CRM_Contribute_PseudoConstant
::contributionStatus(NULL, 'label');
845 $contributionPages = CRM_Contribute_PseudoConstant
::contributionPage();
846 //CRM-16338 if both soft-credit and contribution are enabled then process the contribution's
847 //total amount's average, count and sum separately and add it to the respective result list
848 $softCredit = (!empty($this->_params
['fields']['soft_amount']) && !empty($this->_params
['fields']['total_amount'])) ?
TRUE : FALSE;
850 $this->from('contribution');
851 $this->customDataFrom();
852 $contriSQL = "{$this->_select} {$this->_from} {$this->_where} {$this->_groupBy} {$this->_having} {$this->_orderBy} {$this->_limit}";
853 CRM_Core_DAO
::disableFullGroupByMode();
854 $contriDAO = CRM_Core_DAO
::executeQuery($contriSQL);
855 CRM_Core_DAO
::reenableFullGroupByMode();
856 $this->addToDeveloperTab($contriSQL);
858 'civicrm_contribution_total_amount_sum',
859 'civicrm_contribution_total_amount_avg',
860 'civicrm_contribution_total_amount_count',
863 while ($contriDAO->fetch()) {
864 foreach ($contriFields as $column) {
865 $rows[$count][$column] = $contriDAO->$column;
870 foreach ($rows as $rowNum => $row) {
871 // make count columns point to detail report
872 if (!empty($this->_params
['group_bys']['receive_date']) &&
873 !empty($row['civicrm_contribution_receive_date_start']) &&
874 CRM_Utils_Array
::value('civicrm_contribution_receive_date_start', $row) &&
875 !empty($row['civicrm_contribution_receive_date_subtotal'])
878 $dateStart = CRM_Utils_Date
::customFormat($row['civicrm_contribution_receive_date_start'], '%Y%m%d');
879 $endDate = new DateTime($dateStart);
882 list($dateEnd['Y'], $dateEnd['M'], $dateEnd['d']) = explode(':', $endDate->format('Y:m:d'));
884 switch (strtolower($this->_params
['group_bys_freq']['receive_date'])) {
886 $dateEnd = date("Ymd", mktime(0, 0, 0, $dateEnd['M'] +
1,
887 $dateEnd['d'] - 1, $dateEnd['Y']
892 $dateEnd = date("Ymd", mktime(0, 0, 0, $dateEnd['M'],
893 $dateEnd['d'] - 1, $dateEnd['Y'] +
1
898 $dateEnd = date("Ymd", mktime(0, 0, 0, $dateEnd['M'],
899 $dateEnd['d'] - 1, $dateEnd['Y'] +
1
904 $dateEnd = date("Ymd", mktime(0, 0, 0, $dateEnd['M'],
905 $dateEnd['d'] +
6, $dateEnd['Y']
910 $dateEnd = date("Ymd", mktime(0, 0, 0, $dateEnd['M'] +
3,
911 $dateEnd['d'] - 1, $dateEnd['Y']
915 $url = CRM_Report_Utils_Report
::getNextUrl('contribute/detail',
916 "reset=1&force=1&receive_date_from={$dateStart}&receive_date_to={$dateEnd}",
919 $this->_drilldownReport
921 $rows[$rowNum]['civicrm_contribution_receive_date_start_link'] = $url;
922 $rows[$rowNum]['civicrm_contribution_receive_date_start_hover'] = ts('List all contribution(s) for this date unit.');
926 // make subtotals look nicer
927 if (array_key_exists('civicrm_contribution_receive_date_subtotal', $row) &&
928 !$row['civicrm_contribution_receive_date_subtotal']
930 $this->fixSubTotalDisplay($rows[$rowNum], $this->_statFields
);
934 // convert display name to links
935 if (array_key_exists('civicrm_contact_sort_name', $row) &&
936 array_key_exists('civicrm_contact_id', $row)
938 $url = CRM_Report_Utils_Report
::getNextUrl('contribute/detail',
939 'reset=1&force=1&id_op=eq&id_value=' . $row['civicrm_contact_id'],
940 $this->_absoluteUrl
, $this->_id
, $this->_drilldownReport
942 $rows[$rowNum]['civicrm_contact_sort_name_link'] = $url;
943 $rows[$rowNum]['civicrm_contact_sort_name_hover'] = ts("Lists detailed contribution(s) for this record.");
947 // convert contribution status id to status name
948 if ($value = CRM_Utils_Array
::value('civicrm_contribution_contribution_status_id', $row)) {
949 $rows[$rowNum]['civicrm_contribution_contribution_status_id'] = $contributionStatus[$value];
953 if (!empty($row['civicrm_financial_trxn_card_type_id'])) {
954 $rows[$rowNum]['civicrm_financial_trxn_card_type_id'] = $this->getLabels($row['civicrm_financial_trxn_card_type_id'], 'CRM_Financial_DAO_FinancialTrxn', 'card_type_id');
958 if ($value = CRM_Utils_Array
::value('civicrm_contribution_contribution_page_id', $row)) {
959 $rows[$rowNum]['civicrm_contribution_contribution_page_id'] = $contributionPages[$value];
963 // If using campaigns, convert campaign_id to campaign title
964 if (array_key_exists('civicrm_contribution_campaign_id', $row)) {
965 if ($value = $row['civicrm_contribution_campaign_id']) {
966 $rows[$rowNum]['civicrm_contribution_campaign_id'] = $this->campaigns
[$value];
971 // convert batch id to batch title
972 if (!empty($row['civicrm_batch_batch_id']) && !in_array('Subtotal', $rows[$rowNum])) {
973 $rows[$rowNum]['civicrm_batch_batch_id'] = $this->getLabels($row['civicrm_batch_batch_id'], 'CRM_Batch_BAO_EntityBatch', 'batch_id');
977 $entryFound = $this->alterDisplayAddressFields($row, $rows, $rowNum, 'contribute/detail', 'List all contribution(s) for this ') ?
TRUE : $entryFound;
978 $entryFound = $this->alterDisplayContactFields($row, $rows, $rowNum, 'contribute/detail', 'List all contribution(s) for this ') ?
TRUE : $entryFound;
980 // skip looking further in rows, if first row itself doesn't
981 // have the column we need
991 * Note this is a slow query. Alternative is extended reports.
996 protected function calculateMode($sql) {
998 $modeDAO = CRM_Core_DAO
::executeQuery($sql);
999 while ($modeDAO->fetch()) {
1000 if ($modeDAO->civicrm_contribution_total_amount_count
> 1) {
1001 $mode[] = CRM_Utils_Money
::format($modeDAO->amount
, $modeDAO->currency
);
1013 * Note this is a slow query. Alternative is extended reports.
1015 * @return array|null
1017 protected function calculateMedian() {
1018 $sql = "{$this->_from} {$this->_where}";
1019 $currencies = CRM_Core_OptionGroup
::values('currencies_enabled');
1021 foreach ($currencies as $currency => $val) {
1023 $where = "AND {$this->_aliases['civicrm_contribution']}.currency = '{$currency}'";
1024 $rowCount = CRM_Core_DAO
::singleValueQuery("SELECT count(*) as count {$sql} {$where}");
1028 $medianRow = floor($rowCount / 2);
1029 if ($rowCount %
2 == 0 && !empty($medianRow)) {
1035 $medianValue = "SELECT {$this->_aliases['civicrm_contribution']}.total_amount as median
1037 ORDER BY median LIMIT {$medianRow},{$offset}";
1038 $medianValDAO = CRM_Core_DAO
::executeQuery($medianValue);
1039 while ($medianValDAO->fetch()) {
1041 $midValue = $midValue +
$medianValDAO->median
;
1044 $median[] = CRM_Utils_Money
::format($medianValDAO->median
, $currency);
1048 $midValue = $midValue / 2;
1049 $median[] = CRM_Utils_Money
::format($midValue, $currency);