3 +--------------------------------------------------------------------+
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2019 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
31 * @copyright CiviCRM LLC (c) 2004-2019
33 class CRM_Report_Form_Contribute_Detail
extends CRM_Report_Form
{
35 protected $_summary = NULL;
37 protected $_softFrom = NULL;
39 protected $noDisplayContributionOrSoftColumn = FALSE;
41 protected $_customGroupExtends = [
47 protected $groupConcatTested = TRUE;
49 protected $isTempTableBuilt = FALSE;
54 * This can be 'Main' or 'SoftCredit' to denote which query we are building.
58 protected $queryMode = 'Main';
61 * Is this report being run on contributions as the base entity.
63 * The report structure is generally designed around a base entity but
64 * depending on input it can be run in a sort of hybrid way that causes a lot
67 * If it is in isContributionsOnlyMode we can simplify.
69 * (arguably there should be 2 separate report templates, not one doing double duty.)
73 protected $isContributionBaseMode = FALSE;
76 * This report has been optimised for group filtering.
82 protected $groupFilterNotOptimised = FALSE;
87 public function __construct() {
88 $this->_autoIncludeIndexedFieldsAsOrderBys
= 1;
89 $this->_columns
= array_merge($this->getColumns('Contact', [
90 'order_bys_defaults' => ['sort_name' => 'ASC '],
91 'fields_defaults' => ['sort_name'],
92 'fields_excluded' => ['id'],
93 'fields_required' => ['id'],
94 'filters_defaults' => ['is_deleted' => 0],
95 'no_field_disambiguation' => TRUE,
98 'dao' => 'CRM_Core_DAO_Email',
101 'title' => ts('Donor Email'),
105 'grouping' => 'contact-fields',
107 'civicrm_line_item' => [
108 'dao' => 'CRM_Price_DAO_LineItem',
111 'dao' => 'CRM_Core_DAO_Phone',
114 'title' => ts('Donor Phone'),
119 'grouping' => 'contact-fields',
121 'civicrm_contribution' => [
122 'dao' => 'CRM_Contribute_DAO_Contribution',
124 'contribution_id' => [
126 'no_display' => TRUE,
129 'list_contri_id' => [
131 'title' => ts('Contribution ID'),
133 'financial_type_id' => [
134 'title' => ts('Financial Type'),
137 'contribution_status_id' => [
138 'title' => ts('Contribution Status'),
140 'contribution_page_id' => [
141 'title' => ts('Contribution Page'),
144 'title' => ts('Source'),
146 'payment_instrument_id' => [
147 'title' => ts('Payment Type'),
150 'title' => ts('Check Number'),
154 'no_display' => TRUE,
157 'receive_date' => ['default' => TRUE],
158 'receipt_date' => NULL,
159 'thankyou_date' => NULL,
161 'title' => ts('Amount'),
164 'non_deductible_amount' => [
165 'title' => ts('Non-deductible Amount'),
167 'fee_amount' => NULL,
168 'net_amount' => NULL,
169 'contribution_or_soft' => [
170 'title' => ts('Contribution OR Soft Credit?'),
171 'dbAlias' => "'Contribution'",
174 'title' => ts('Soft Credits'),
177 'soft_credit_for' => [
178 'title' => ts('Soft Credit For'),
182 'title' => ts('Cancelled / Refunded Date'),
183 'name' => 'contribution_cancel_date',
186 'title' => ts('Cancellation / Refund Reason'),
190 'contribution_or_soft' => [
191 'title' => ts('Contribution OR Soft Credit?'),
193 'operatorType' => CRM_Report_Form
::OP_SELECT
,
194 'type' => CRM_Utils_Type
::T_STRING
,
196 'contributions_only' => ts('Contributions Only'),
197 'soft_credits_only' => ts('Soft Credits Only'),
198 'both' => ts('Both'),
201 'receive_date' => ['operatorType' => CRM_Report_Form
::OP_DATE
],
202 'thankyou_date' => ['operatorType' => CRM_Report_Form
::OP_DATE
],
203 'contribution_source' => [
204 'title' => ts('Source'),
206 'type' => CRM_Utils_Type
::T_STRING
,
209 'title' => ts('Currency'),
210 'operatorType' => CRM_Report_Form
::OP_MULTISELECT
,
211 'options' => CRM_Core_OptionGroup
::values('currencies_enabled'),
213 'type' => CRM_Utils_Type
::T_STRING
,
215 'non_deductible_amount' => [
216 'title' => ts('Non-deductible Amount'),
218 'financial_type_id' => [
219 'title' => ts('Financial Type'),
220 'operatorType' => CRM_Report_Form
::OP_MULTISELECT
,
221 'options' => CRM_Financial_BAO_FinancialType
::getAvailableFinancialTypes(),
222 'type' => CRM_Utils_Type
::T_INT
,
224 'contribution_page_id' => [
225 'title' => ts('Contribution Page'),
226 'operatorType' => CRM_Report_Form
::OP_MULTISELECT
,
227 'options' => CRM_Contribute_PseudoConstant
::contributionPage(),
228 'type' => CRM_Utils_Type
::T_INT
,
230 'payment_instrument_id' => [
231 'title' => ts('Payment Type'),
232 'operatorType' => CRM_Report_Form
::OP_MULTISELECT
,
233 'options' => CRM_Contribute_PseudoConstant
::paymentInstrument(),
234 'type' => CRM_Utils_Type
::T_INT
,
236 'contribution_status_id' => [
237 'title' => ts('Contribution Status'),
238 'operatorType' => CRM_Report_Form
::OP_MULTISELECT
,
239 'options' => CRM_Contribute_PseudoConstant
::contributionStatus(),
241 'type' => CRM_Utils_Type
::T_INT
,
243 'total_amount' => ['title' => ts('Contribution Amount')],
245 'title' => ts('Cancelled / Refunded Date'),
246 'operatorType' => CRM_Report_Form
::OP_DATE
,
247 'name' => 'contribution_cancel_date',
250 'title' => ts('Cancellation / Refund Reason'),
254 'financial_type_id' => ['title' => ts('Financial Type')],
255 'contribution_status_id' => ['title' => ts('Contribution Status')],
256 'payment_instrument_id' => ['title' => ts('Payment Method')],
257 'receive_date' => ['title' => ts('Date Received')],
258 'thankyou_date' => ['title' => ts('Thank-you Date')],
261 'contribution_id' => [
265 'title' => ts('Contribution'),
268 'grouping' => 'contri-fields',
270 'civicrm_contribution_soft' => [
271 'dao' => 'CRM_Contribute_DAO_ContributionSoft',
273 'soft_credit_type_id' => ['title' => ts('Soft Credit Type')],
274 'soft_credit_amount' => ['title' => ts('Soft Credit amount'), 'name' => 'amount', 'type' => CRM_Utils_Type
::T_MONEY
],
277 'soft_credit_type_id' => [
278 'title' => ts('Soft Credit Type'),
279 'operatorType' => CRM_Report_Form
::OP_MULTISELECT
,
280 'options' => CRM_Core_OptionGroup
::values('soft_credit_type'),
282 'type' => CRM_Utils_Type
::T_STRING
,
286 'soft_credit_id' => [
288 'title' => ts('Soft Credit'),
292 'civicrm_financial_trxn' => [
293 'dao' => 'CRM_Financial_DAO_FinancialTrxn',
296 'title' => ts('Credit Card Type'),
301 'title' => ts('Credit Card Type'),
302 'operatorType' => CRM_Report_Form
::OP_MULTISELECT
,
303 'options' => CRM_Financial_DAO_FinancialTrxn
::buildOptions('card_type_id'),
305 'type' => CRM_Utils_Type
::T_STRING
,
310 'dao' => 'CRM_Batch_DAO_EntityBatch',
311 'grouping' => 'contri-fields',
314 'name' => 'batch_id',
315 'title' => ts('Batch Name'),
320 'title' => ts('Batch Name'),
321 'operatorType' => CRM_Report_Form
::OP_MULTISELECT
,
322 'options' => CRM_Batch_BAO_Batch
::getBatches(),
323 'type' => CRM_Utils_Type
::T_INT
,
324 'dbAlias' => 'batch_civireport.batch_id',
328 'civicrm_contribution_ordinality' => [
329 'dao' => 'CRM_Contribute_DAO_Contribution',
330 'alias' => 'cordinality',
333 'title' => ts('Contribution Ordinality'),
334 'operatorType' => CRM_Report_Form
::OP_MULTISELECT
,
336 0 => 'First by Contributor',
337 1 => 'Second or Later by Contributor',
339 'type' => CRM_Utils_Type
::T_INT
,
344 'dao' => 'CRM_Core_DAO_Note',
346 'contribution_note' => [
348 'title' => ts('Contribution Note'),
354 'title' => ts('Contribution Note'),
355 'operator' => 'like',
356 'type' => CRM_Utils_Type
::T_STRING
,
360 ]) +
$this->addAddressFields(FALSE);
361 // The tests test for this variation of the sort_name field. Don't argue with the tests :-).
362 $this->_columns
['civicrm_contact']['fields']['sort_name']['title'] = ts('Donor Name');
363 $this->_groupFilter
= TRUE;
364 $this->_tagFilter
= TRUE;
365 // If we have campaigns enabled, add those elements to both the fields, filters and sorting
366 $this->addCampaignFields('civicrm_contribution', FALSE, TRUE);
368 $this->_currencyColumn
= 'civicrm_contribution_currency';
369 parent
::__construct();
373 * Validate incompatible report settings.
376 * true if no error found
378 public function validate() {
379 // If you're displaying Contributions Only, you can't group by soft credit.
380 $contributionOrSoftVal = $this->getElementValue('contribution_or_soft_value');
381 if ($contributionOrSoftVal[0] == 'contributions_only') {
382 $groupBySoft = $this->getElementValue('group_bys');
383 if (!empty($groupBySoft['soft_credit_id'])) {
384 $this->setElementError('group_bys', ts('You cannot group by soft credit when displaying contributions only. Please uncheck "Soft Credit" in the Grouping tab.'));
388 return parent
::validate();
392 * Set the FROM clause for the report.
394 public function from() {
395 $this->setFromBase('civicrm_contact');
397 INNER JOIN civicrm_contribution {$this->_aliases['civicrm_contribution']}
398 ON {$this->_aliases['civicrm_contact']}.id = {$this->_aliases['civicrm_contribution']}.contact_id
399 AND {$this->_aliases['civicrm_contribution']}.is_test = 0";
401 $this->joinContributionToSoftCredit();
402 $this->appendAdditionalFromJoins();
410 public function statistics(&$rows) {
411 $statistics = parent
::statistics($rows);
413 $totalAmount = $average = $fees = $net = [];
416 SELECT COUNT({$this->_aliases['civicrm_contribution']}.total_amount ) as count,
417 SUM( {$this->_aliases['civicrm_contribution']}.total_amount ) as amount,
418 ROUND(AVG({$this->_aliases['civicrm_contribution']}.total_amount), 2) as avg,
419 {$this->_aliases['civicrm_contribution']}.currency as currency,
420 SUM( {$this->_aliases['civicrm_contribution']}.fee_amount ) as fees,
421 SUM( {$this->_aliases['civicrm_contribution']}.net_amount ) as net
424 $group = "\nGROUP BY {$this->_aliases['civicrm_contribution']}.currency";
425 $sql = "{$select} {$this->_from} {$this->_where} {$group}";
426 $dao = CRM_Core_DAO
::executeQuery($sql);
427 $this->addToDeveloperTab($sql);
429 while ($dao->fetch()) {
430 $totalAmount[] = CRM_Utils_Money
::format($dao->amount
, $dao->currency
) . " (" . $dao->count
. ")";
431 $fees[] = CRM_Utils_Money
::format($dao->fees
, $dao->currency
);
432 $net[] = CRM_Utils_Money
::format($dao->net
, $dao->currency
);
433 $average[] = CRM_Utils_Money
::format($dao->avg
, $dao->currency
);
434 $count +
= $dao->count
;
436 $statistics['counts']['amount'] = [
437 'title' => ts('Total Amount (Contributions)'),
438 'value' => implode(', ', $totalAmount),
439 'type' => CRM_Utils_Type
::T_STRING
,
441 $statistics['counts']['count'] = [
442 'title' => ts('Total Contributions'),
445 $statistics['counts']['fees'] = [
446 'title' => ts('Fees'),
447 'value' => implode(', ', $fees),
448 'type' => CRM_Utils_Type
::T_STRING
,
450 $statistics['counts']['net'] = [
451 'title' => ts('Net'),
452 'value' => implode(', ', $net),
453 'type' => CRM_Utils_Type
::T_STRING
,
455 $statistics['counts']['avg'] = [
456 'title' => ts('Average'),
457 'value' => implode(', ', $average),
458 'type' => CRM_Utils_Type
::T_STRING
,
461 // Stats for soft credits
462 if ($this->_softFrom
&&
463 CRM_Utils_Array
::value('contribution_or_soft_value', $this->_params
) !=
466 $totalAmount = $average = [];
469 SELECT COUNT(contribution_soft_civireport.amount ) as count,
470 SUM(contribution_soft_civireport.amount ) as amount,
471 ROUND(AVG(contribution_soft_civireport.amount), 2) as avg,
472 {$this->_aliases['civicrm_contribution']}.currency as currency";
476 GROUP BY {$this->_aliases['civicrm_contribution']}.currency";
477 $dao = CRM_Core_DAO
::executeQuery($sql);
478 $this->addToDeveloperTab($sql);
479 while ($dao->fetch()) {
480 $totalAmount[] = CRM_Utils_Money
::format($dao->amount
, $dao->currency
) . " (" .
482 $average[] = CRM_Utils_Money
::format($dao->avg
, $dao->currency
);
483 $count +
= $dao->count
;
485 $statistics['counts']['softamount'] = [
486 'title' => ts('Total Amount (Soft Credits)'),
487 'value' => implode(', ', $totalAmount),
488 'type' => CRM_Utils_Type
::T_STRING
,
490 $statistics['counts']['softcount'] = [
491 'title' => ts('Total Soft Credits'),
494 $statistics['counts']['softavg'] = [
495 'title' => ts('Average (Soft Credits)'),
496 'value' => implode(', ', $average),
497 'type' => CRM_Utils_Type
::T_STRING
,
505 * Build the report query.
507 * @param bool $applyLimit
511 public function buildQuery($applyLimit = FALSE) {
512 if ($this->isTempTableBuilt
) {
514 return "SELECT SQL_CALC_FOUND_ROWS * FROM {$this->temporaryTables['civireport_contribution_detail_temp3']['name']} $this->_orderBy $this->_limit";
516 return parent
::buildQuery($applyLimit);
520 * Shared function for preliminary processing.
522 * This is called by the api / unit tests and the form layer and is
523 * the right place to do 'initial analysis of input'.
525 public function beginPostProcessCommon() {
526 // CRM-18312 - display soft_credits and soft_credits_for column
527 // when 'Contribution or Soft Credit?' column is not selected
528 if (empty($this->_params
['fields']['contribution_or_soft'])) {
529 $this->_params
['fields']['contribution_or_soft'] = 1;
530 $this->noDisplayContributionOrSoftColumn
= TRUE;
533 if (CRM_Utils_Array
::value('contribution_or_soft_value', $this->_params
, 'contributions_only') == 'contributions_only') {
534 $this->isContributionBaseMode
= TRUE;
536 if ($this->isContributionBaseMode
&&
537 (!empty($this->_params
['fields']['soft_credit_type_id'])
538 ||
!empty($this->_params
['soft_credit_type_id_value']))
540 unset($this->_params
['fields']['soft_credit_type_id']);
541 if (!empty($this->_params
['soft_credit_type_id_value'])) {
542 $this->_params
['soft_credit_type_id_value'] = [];
543 CRM_Core_Session
::setStatus(ts('Is it not possible to filter on soft contribution type when not including soft credits.'));
546 // 1. use main contribution query to build temp table 1
547 $sql = $this->buildQuery();
548 $this->createTemporaryTable('civireport_contribution_detail_temp1', $sql);
550 // 2. customize main contribution query for soft credit, and build temp table 2 with soft credit contributions only
551 $this->queryMode
= 'SoftCredit';
552 // Rebuild select with no groupby. Do not let column headers change.
553 $headers = $this->_columnHeaders
;
555 $this->_columnHeaders
= $headers;
556 $this->softCreditFrom();
557 // also include custom group from if included
558 // since this might be included in select
559 $this->customDataFrom();
561 $select = str_ireplace('contribution_civireport.total_amount', 'contribution_soft_civireport.amount', $this->_select
);
562 $select = str_ireplace("'Contribution' as", "'Soft Credit' as", $select);
564 // we inner join with temp1 to restrict soft contributions to those in temp1 table.
565 // no group by here as we want to display as many soft credit rows as actually exist.
566 $sql = "{$select} {$this->_from} {$this->_where} $this->_groupBy";
567 $this->createTemporaryTable('civireport_contribution_detail_temp2', $sql);
569 if (CRM_Utils_Array
::value('contribution_or_soft_value', $this->_params
) ==
572 // revise pager : prev, next based on soft-credits only
576 // copy _from for later use of stats calculation for soft credits, and reset $this->_from to main query
577 $this->_softFrom
= $this->_from
;
579 // simple reset of ->_from
582 // also include custom group from if included
583 // since this might be included in select
584 $this->customDataFrom();
586 // 3. Decide where to populate temp3 table from
587 if ($this->isContributionBaseMode
589 $this->createTemporaryTable('civireport_contribution_detail_temp3',
590 "(SELECT * FROM {$this->temporaryTables['civireport_contribution_detail_temp1']['name']})"
593 elseif (CRM_Utils_Array
::value('contribution_or_soft_value', $this->_params
) ==
596 $this->createTemporaryTable('civireport_contribution_detail_temp3',
597 "(SELECT * FROM {$this->temporaryTables['civireport_contribution_detail_temp2']['name']})"
601 $this->createTemporaryTable('civireport_contribution_detail_temp3', "
602 (SELECT * FROM {$this->temporaryTables['civireport_contribution_detail_temp1']['name']})
604 (SELECT * FROM {$this->temporaryTables['civireport_contribution_detail_temp2']['name']})");
606 $this->isTempTableBuilt
= TRUE;
610 * Store group bys into array - so we can check elsewhere what is grouped.
612 * If we are generating a table of soft credits we need to group by them.
614 protected function storeGroupByArray() {
615 if ($this->queryMode
=== 'SoftCredit') {
616 $this->_groupByArray
= [$this->_aliases
['civicrm_contribution_soft'] . '.id'];
619 parent
::storeGroupByArray();
624 * Alter display of rows.
626 * Iterate through the rows retrieved via SQL and make changes for display purposes,
627 * such as rendering contacts as links.
630 * Rows generated by SQL, with an array for each row.
632 public function alterDisplay(&$rows) {
634 $display_flag = $prev_cid = $cid = 0;
635 $contributionTypes = CRM_Contribute_PseudoConstant
::financialType();
636 $contributionStatus = CRM_Contribute_PseudoConstant
::contributionStatus();
637 $paymentInstruments = CRM_Contribute_PseudoConstant
::paymentInstrument();
638 $contributionPages = CRM_Contribute_PseudoConstant
::contributionPage();
639 $batches = CRM_Batch_BAO_Batch
::getBatches();
640 foreach ($rows as $rowNum => $row) {
641 if (!empty($this->_noRepeats
) && $this->_outputMode
!= 'csv') {
642 // don't repeat contact details if its same as the previous row
643 if (array_key_exists('civicrm_contact_id', $row)) {
644 if ($cid = $row['civicrm_contact_id']) {
649 if ($prev_cid == $cid) {
660 foreach ($row as $colName => $colVal) {
661 // Hide repeats in no-repeat columns, but not if the field's a section header
662 if (in_array($colName, $this->_noRepeats
) &&
663 !array_key_exists($colName, $this->_sections
)
665 unset($rows[$rowNum][$colName]);
674 if (CRM_Utils_Array
::value('civicrm_contribution_contribution_or_soft', $rows[$rowNum]) ==
677 unset($rows[$rowNum]['civicrm_contribution_soft_soft_credit_type_id']);
680 $entryFound = $this->alterDisplayContactFields($row, $rows, $rowNum, 'contribution/detail', ts('View Contribution Details')) ?
TRUE : $entryFound;
681 // convert donor sort name to link
682 if (array_key_exists('civicrm_contact_sort_name', $row) &&
683 !empty($rows[$rowNum]['civicrm_contact_sort_name']) &&
684 array_key_exists('civicrm_contact_id', $row)
686 $url = CRM_Utils_System
::url("civicrm/contact/view",
687 'reset=1&cid=' . $row['civicrm_contact_id'],
690 $rows[$rowNum]['civicrm_contact_sort_name_link'] = $url;
691 $rows[$rowNum]['civicrm_contact_sort_name_hover'] = ts("View Contact Summary for this Contact.");
694 if ($value = CRM_Utils_Array
::value('civicrm_contribution_financial_type_id', $row)) {
695 $rows[$rowNum]['civicrm_contribution_financial_type_id'] = $contributionTypes[$value];
698 if ($value = CRM_Utils_Array
::value('civicrm_contribution_contribution_status_id', $row)) {
699 $rows[$rowNum]['civicrm_contribution_contribution_status_id'] = $contributionStatus[$value];
702 if ($value = CRM_Utils_Array
::value('civicrm_contribution_contribution_page_id', $row)) {
703 $rows[$rowNum]['civicrm_contribution_contribution_page_id'] = $contributionPages[$value];
706 if ($value = CRM_Utils_Array
::value('civicrm_contribution_payment_instrument_id', $row)) {
707 $rows[$rowNum]['civicrm_contribution_payment_instrument_id'] = $paymentInstruments[$value];
710 if (!empty($row['civicrm_batch_batch_id'])) {
711 $rows[$rowNum]['civicrm_batch_batch_id'] = CRM_Utils_Array
::value($row['civicrm_batch_batch_id'], $batches);
714 if (!empty($row['civicrm_financial_trxn_card_type_id'])) {
715 $rows[$rowNum]['civicrm_financial_trxn_card_type_id'] = $this->getLabels($row['civicrm_financial_trxn_card_type_id'], 'CRM_Financial_DAO_FinancialTrxn', 'card_type_id');
719 // Contribution amount links to viewing contribution
720 if ($value = CRM_Utils_Array
::value('civicrm_contribution_total_amount', $row)) {
721 $rows[$rowNum]['civicrm_contribution_total_amount'] = CRM_Utils_Money
::format($value, $row['civicrm_contribution_currency']);
722 if (CRM_Core_Permission
::check('access CiviContribute')) {
723 $url = CRM_Utils_System
::url(
724 "civicrm/contact/view/contribution",
727 'id' => $row['civicrm_contribution_contribution_id'],
728 'cid' => $row['civicrm_contact_id'],
730 'context' => 'contribution',
731 'selectedChild' => 'contribute',
735 $rows[$rowNum]['civicrm_contribution_total_amount_link'] = $url;
736 $rows[$rowNum]['civicrm_contribution_total_amount_hover'] = ts("View Details of this Contribution.");
741 // convert campaign_id to campaign title
742 if (array_key_exists('civicrm_contribution_campaign_id', $row)) {
743 if ($value = $row['civicrm_contribution_campaign_id']) {
744 $rows[$rowNum]['civicrm_contribution_campaign_id'] = $this->campaigns
[$value];
750 if (array_key_exists('civicrm_contribution_soft_credits', $row) &&
752 CRM_Utils_Array
::value('civicrm_contribution_contribution_or_soft', $rows[$rowNum]) &&
753 array_key_exists('civicrm_contribution_contribution_id', $row)
756 SELECT civicrm_contact_id, civicrm_contact_sort_name, civicrm_contribution_total_amount, civicrm_contribution_currency
757 FROM {$this->temporaryTables['civireport_contribution_detail_temp2']['name']}
758 WHERE civicrm_contribution_contribution_id={$row['civicrm_contribution_contribution_id']}";
759 $dao = CRM_Core_DAO
::executeQuery($query);
761 $separator = ($this->_outputMode
!== 'csv') ?
"<br/>" : ' ';
762 while ($dao->fetch()) {
763 $url = CRM_Utils_System
::url("civicrm/contact/view", 'reset=1&cid=' .
764 $dao->civicrm_contact_id
);
765 $string = $string . ($string ?
$separator : '') .
766 "<a href='{$url}'>{$dao->civicrm_contact_sort_name}</a> " .
767 CRM_Utils_Money
::format($dao->civicrm_contribution_total_amount
, $dao->civicrm_contribution_currency
);
769 $rows[$rowNum]['civicrm_contribution_soft_credits'] = $string;
772 if (array_key_exists('civicrm_contribution_soft_credit_for', $row) &&
774 CRM_Utils_Array
::value('civicrm_contribution_contribution_or_soft', $rows[$rowNum]) &&
775 array_key_exists('civicrm_contribution_contribution_id', $row)
778 SELECT civicrm_contact_id, civicrm_contact_sort_name
779 FROM {$this->temporaryTables['civireport_contribution_detail_temp1']['name']}
780 WHERE civicrm_contribution_contribution_id={$row['civicrm_contribution_contribution_id']}";
781 $dao = CRM_Core_DAO
::executeQuery($query);
783 while ($dao->fetch()) {
784 $url = CRM_Utils_System
::url("civicrm/contact/view", 'reset=1&cid=' .
785 $dao->civicrm_contact_id
);
787 "\n<a href='{$url}'>{$dao->civicrm_contact_sort_name}</a>";
789 $rows[$rowNum]['civicrm_contribution_soft_credit_for'] = $string;
792 // CRM-18312 - hide 'contribution_or_soft' column if unchecked.
793 if (!empty($this->noDisplayContributionOrSoftColumn
)) {
794 unset($rows[$rowNum]['civicrm_contribution_contribution_or_soft']);
795 unset($this->_columnHeaders
['civicrm_contribution_contribution_or_soft']);
798 //convert soft_credit_type_id into label
799 if (array_key_exists('civicrm_contribution_soft_soft_credit_type_id', $rows[$rowNum])) {
800 $rows[$rowNum]['civicrm_contribution_soft_soft_credit_type_id'] = CRM_Core_PseudoConstant
::getLabel(
801 'CRM_Contribute_BAO_ContributionSoft',
802 'soft_credit_type_id',
803 $row['civicrm_contribution_soft_soft_credit_type_id']
807 $entryFound = $this->alterDisplayAddressFields($row, $rows, $rowNum, 'contribute/detail', 'List all contribution(s) for this ') ?
TRUE : $entryFound;
809 // skip looking further in rows, if first row itself doesn't
810 // have the column we need
818 public function sectionTotals() {
820 // Reports using order_bys with sections must populate $this->_selectAliases in select() method.
821 if (empty($this->_selectAliases
)) {
825 if (!empty($this->_sections
)) {
826 // build the query with no LIMIT clause
827 $select = str_ireplace('SELECT SQL_CALC_FOUND_ROWS ', 'SELECT ', $this->_select
);
828 $sql = "{$select} {$this->_from} {$this->_where} {$this->_groupBy} {$this->_having} {$this->_orderBy}";
830 // pull section aliases out of $this->_sections
831 $sectionAliases = array_keys($this->_sections
);
834 foreach (array_merge($sectionAliases, $this->_selectAliases
) as $alias) {
835 $ifnulls[] = "ifnull($alias, '') as $alias";
837 $this->_select
= "SELECT " . implode(", ", $ifnulls);
838 $this->_select
= CRM_Contact_BAO_Query
::appendAnyValueToSelect($ifnulls, $sectionAliases);
840 /* Group (un-limited) report by all aliases and get counts. This might
841 * be done more efficiently when the contents of $sql are known, ie. by
842 * overriding this method in the report class.
847 if (array_search("civicrm_contribution_total_amount", $this->_selectAliases
) !==
850 $addtotals = ", sum(civicrm_contribution_total_amount) as sumcontribs";
851 $showsumcontribs = TRUE;
854 $query = $this->_select
.
855 "$addtotals, count(*) as ct from {$this->temporaryTables['civireport_contribution_detail_temp3']['name']} group by " .
856 implode(", ", $sectionAliases);
857 // initialize array of total counts
858 $sumcontribs = $totals = [];
859 $dao = CRM_Core_DAO
::executeQuery($query);
860 $this->addToDeveloperTab($query);
861 while ($dao->fetch()) {
863 // let $this->_alterDisplay translate any integer ids to human-readable values.
864 $rows[0] = $dao->toArray();
865 $this->alterDisplay($rows);
868 // add totals for all permutations of section values
871 $aliasCount = count($sectionAliases);
872 foreach ($sectionAliases as $alias) {
873 $values[] = $row[$alias];
874 $key = implode(CRM_Core_DAO
::VALUE_SEPARATOR
, $values);
875 if ($i == $aliasCount) {
876 // the last alias is the lowest-level section header; use count as-is
877 $totals[$key] = $dao->ct
;
878 if ($showsumcontribs) {
879 $sumcontribs[$key] = $dao->sumcontribs
;
883 // other aliases are higher level; roll count into their total
884 $totals[$key] = (array_key_exists($key, $totals)) ?
$totals[$key] +
$dao->ct
: $dao->ct
;
885 if ($showsumcontribs) {
886 $sumcontribs[$key] = array_key_exists($key, $sumcontribs) ?
$sumcontribs[$key] +
$dao->sumcontribs
: $dao->sumcontribs
;
891 if ($showsumcontribs) {
893 // ts exception to avoid having ts("%1 %2: %3")
894 $title = '%1 contributions / soft-credits: %2';
896 if (CRM_Utils_Array
::value('contribution_or_soft_value', $this->_params
) ==
899 $title = '%1 contributions: %2';
901 elseif (CRM_Utils_Array
::value('contribution_or_soft_value', $this->_params
) ==
904 $title = '%1 soft-credits: %2';
906 foreach ($totals as $key => $total) {
907 $totalandsum[$key] = ts($title, [
909 2 => CRM_Utils_Money
::format($sumcontribs[$key]),
912 $this->assign('sectionTotals', $totalandsum);
915 $this->assign('sectionTotals', $totals);
921 * Generate the from clause as it relates to the soft credits.
923 public function softCreditFrom() {
926 FROM {$this->temporaryTables['civireport_contribution_detail_temp1']['name']} temp1_civireport
927 INNER JOIN civicrm_contribution {$this->_aliases['civicrm_contribution']}
928 ON temp1_civireport.civicrm_contribution_contribution_id = {$this->_aliases['civicrm_contribution']}.id
929 INNER JOIN civicrm_contribution_soft contribution_soft_civireport
930 ON contribution_soft_civireport.contribution_id = {$this->_aliases['civicrm_contribution']}.id
931 INNER JOIN civicrm_contact {$this->_aliases['civicrm_contact']}
932 ON {$this->_aliases['civicrm_contact']}.id = contribution_soft_civireport.contact_id
936 //Join temp table if report is filtered by group. This is specific to 'notin' operator and covered in unit test(ref dev/core#212)
937 if (!empty($this->_params
['gid_op']) && $this->_params
['gid_op'] == 'notin') {
938 $this->joinGroupTempTable('civicrm_contact', 'id', $this->_aliases
['civicrm_contact']);
940 $this->appendAdditionalFromJoins();
944 * Append the joins that are required regardless of context.
946 public function appendAdditionalFromJoins() {
947 if (!empty($this->_params
['ordinality_value'])) {
949 INNER JOIN (SELECT c.id, IF(COUNT(oc.id) = 0, 0, 1) AS ordinality FROM civicrm_contribution c LEFT JOIN civicrm_contribution oc ON c.contact_id = oc.contact_id AND oc.receive_date < c.receive_date GROUP BY c.id) {$this->_aliases['civicrm_contribution_ordinality']}
950 ON {$this->_aliases['civicrm_contribution_ordinality']}.id = {$this->_aliases['civicrm_contribution']}.id";
952 $this->joinPhoneFromContact();
953 $this->joinAddressFromContact();
954 $this->joinEmailFromContact();
956 // include contribution note
957 if (!empty($this->_params
['fields']['contribution_note']) ||
958 !empty($this->_params
['note_value'])
961 LEFT JOIN civicrm_note {$this->_aliases['civicrm_note']}
962 ON ( {$this->_aliases['civicrm_note']}.entity_table = 'civicrm_contribution' AND
963 {$this->_aliases['civicrm_contribution']}.id = {$this->_aliases['civicrm_note']}.entity_id )";
965 //for contribution batches
966 if (!empty($this->_params
['fields']['batch_id']) ||
967 !empty($this->_params
['bid_value'])
970 LEFT JOIN civicrm_entity_financial_trxn eft
971 ON eft.entity_id = {$this->_aliases['civicrm_contribution']}.id AND
972 eft.entity_table = 'civicrm_contribution'
973 LEFT JOIN civicrm_entity_batch {$this->_aliases['civicrm_batch']}
974 ON ({$this->_aliases['civicrm_batch']}.entity_id = eft.financial_trxn_id
975 AND {$this->_aliases['civicrm_batch']}.entity_table = 'civicrm_financial_trxn')";
977 // for credit card type
978 $this->addFinancialTrxnFromClause();
982 * Add join to the soft credit table.
984 protected function joinContributionToSoftCredit() {
985 if (CRM_Utils_Array
::value('contribution_or_soft_value', $this->_params
) == 'contributions_only'
986 && !$this->isTableSelected('civicrm_contribution_soft')) {
989 $joinType = ' LEFT ';
990 if (CRM_Utils_Array
::value('contribution_or_soft_value', $this->_params
) == 'soft_credits_only') {
991 $joinType = ' INNER ';
994 $joinType JOIN civicrm_contribution_soft {$this->_aliases['civicrm_contribution_soft']}
995 ON {$this->_aliases['civicrm_contribution_soft']}.contribution_id = {$this->_aliases['civicrm_contribution']}.id