CRM-19175 fix add to group when viewing contribution detail without soft credits.
[civicrm-core.git] / CRM / Report / Form / Contribute / Detail.php
CommitLineData
6a488035 1<?php
6a488035
TO
2/*
3 +--------------------------------------------------------------------+
7e9e8871 4 | CiviCRM version 4.7 |
6a488035 5 +--------------------------------------------------------------------+
fa938177 6 | Copyright CiviCRM LLC (c) 2004-2016 |
6a488035
TO
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
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. |
13 | |
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. |
18 | |
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 +--------------------------------------------------------------------+
d25dd0ee 26 */
6a488035
TO
27
28/**
29 *
30 * @package CRM
fa938177 31 * @copyright CiviCRM LLC (c) 2004-2016
6a488035
TO
32 */
33class CRM_Report_Form_Contribute_Detail extends CRM_Report_Form {
34 protected $_addressField = FALSE;
35
36 protected $_emailField = FALSE;
6a488035
TO
37
38 protected $_summary = NULL;
39
e9b25936
E
40 protected $_softFrom = NULL;
41
70bea8e2 42 protected $_customGroupExtends = array(
43 'Contact',
44 'Individual',
7d793900 45 'Contribution',
70bea8e2 46 );
6a488035 47
74cf4551 48 /**
74cf4551 49 */
00be9182 50 public function __construct() {
70bea8e2 51 $this->_autoIncludeIndexedFieldsAsOrderBys = 1;
f813f78e 52 // Check if CiviCampaign is a) enabled and b) has active campaigns
6a488035
TO
53 $config = CRM_Core_Config::singleton();
54 $campaignEnabled = in_array("CiviCampaign", $config->enableComponents);
55 if ($campaignEnabled) {
56 $getCampaigns = CRM_Campaign_BAO_Campaign::getPermissionedCampaigns(NULL, NULL, TRUE, FALSE, TRUE);
57 $this->activeCampaigns = $getCampaigns['campaigns'];
58 asort($this->activeCampaigns);
59 }
60 $this->_columns = array(
a1a2a83d
TO
61 'civicrm_contact' => array(
62 'dao' => 'CRM_Contact_DAO_Contact',
a703d90c 63 'fields' => $this->getBasicContactFields(),
a1a2a83d
TO
64 'filters' => array(
65 'sort_name' => array(
66 'title' => ts('Donor Name'),
67 'operator' => 'like',
68 ),
69 'id' => array(
70 'title' => ts('Contact ID'),
71 'no_display' => TRUE,
72 'type' => CRM_Utils_Type::T_INT,
6a488035 73 ),
70bea8e2 74 'gender_id' => array(
75 'title' => ts('Gender'),
76 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
77 'options' => CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'gender_id'),
78 ),
79 'birth_date' => array(
80 'title' => ts('Birth Date'),
81 'operatorType' => CRM_Report_Form::OP_DATE,
82 ),
83 'contact_type' => array(
84 'title' => ts('Contact Type'),
85 ),
86 'contact_sub_type' => array(
87 'title' => ts('Contact Subtype'),
88 ),
6a488035 89 ),
70bea8e2 90 'grouping' => 'contact-fields',
a1a2a83d
TO
91 'order_bys' => array(
92 'sort_name' => array(
93 'title' => ts('Last Name, First Name'),
94 'default' => '1',
95 'default_weight' => '0',
96 'default_order' => 'ASC',
a0e67d3c 97 ),
70bea8e2 98 'first_name' => array(
99 'name' => 'first_name',
100 'title' => ts('First Name'),
101 ),
102 'gender_id' => array(
103 'name' => 'gender_id',
104 'title' => ts('Gender'),
105 ),
106 'birth_date' => array(
107 'name' => 'birth_date',
108 'title' => ts('Birth Date'),
109 ),
110 'contact_type' => array(
111 'title' => ts('Contact Type'),
112 ),
113 'contact_sub_type' => array(
114 'title' => ts('Contact Subtype'),
115 ),
6a488035 116 ),
70bea8e2 117
a1a2a83d
TO
118 ),
119 'civicrm_email' => array(
120 'dao' => 'CRM_Core_DAO_Email',
121 'fields' => array(
122 'email' => array(
123 'title' => ts('Donor Email'),
124 'default' => TRUE,
1ebef749 125 ),
a1a2a83d
TO
126 ),
127 'grouping' => 'contact-fields',
128 ),
4a39d437
E
129 'civicrm_line_item' => array(
130 'dao' => 'CRM_Price_DAO_LineItem',
131 ),
a1a2a83d
TO
132 'civicrm_phone' => array(
133 'dao' => 'CRM_Core_DAO_Phone',
134 'fields' => array(
135 'phone' => array(
136 'title' => ts('Donor Phone'),
137 'default' => TRUE,
138 'no_repeat' => TRUE,
139 ),
140 ),
141 'grouping' => 'contact-fields',
142 ),
143 'civicrm_contribution' => array(
144 'dao' => 'CRM_Contribute_DAO_Contribution',
145 'fields' => array(
146 'contribution_id' => array(
147 'name' => 'id',
148 'no_display' => TRUE,
149 'required' => TRUE,
150 ),
151 'list_contri_id' => array(
152 'name' => 'id',
153 'title' => ts('Contribution ID'),
154 ),
155 'financial_type_id' => array(
156 'title' => ts('Financial Type'),
157 'default' => TRUE,
158 ),
159 'contribution_status_id' => array(
160 'title' => ts('Contribution Status'),
161 ),
162 'contribution_page_id' => array(
163 'title' => ts('Contribution Page'),
164 ),
165 'source' => array(
166 'title' => ts('Source'),
167 ),
168 'payment_instrument_id' => array(
169 'title' => ts('Payment Type'),
170 ),
171 'check_number' => array(
172 'title' => ts('Check Number'),
173 ),
174 'currency' => array(
175 'required' => TRUE,
176 'no_display' => TRUE,
177 ),
178 'trxn_id' => NULL,
179 'receive_date' => array('default' => TRUE),
180 'receipt_date' => NULL,
181 'total_amount' => array(
182 'title' => ts('Amount'),
183 'required' => TRUE,
184 'statistics' => array('sum' => ts('Amount')),
6a488035 185 ),
a1a2a83d
TO
186 'fee_amount' => NULL,
187 'net_amount' => NULL,
188 'contribution_or_soft' => array(
189 'title' => ts('Contribution OR Soft Credit?'),
190 'dbAlias' => "'Contribution'",
191 ),
192 'soft_credits' => array(
193 'title' => ts('Soft Credits'),
194 'dbAlias' => "NULL",
195 ),
196 'soft_credit_for' => array(
197 'title' => ts('Soft Credit For'),
198 'dbAlias' => "NULL",
6a488035 199 ),
6a488035 200 ),
a1a2a83d
TO
201 'filters' => array(
202 'contribution_or_soft' => array(
203 'title' => ts('Contribution OR Soft Credit?'),
204 'clause' => "(1)",
205 'operatorType' => CRM_Report_Form::OP_SELECT,
206 'type' => CRM_Utils_Type::T_STRING,
207 'options' => array(
a1a2a83d
TO
208 'contributions_only' => ts('Contributions Only'),
209 'soft_credits_only' => ts('Soft Credits Only'),
f072e08f 210 'both' => ts('Both'),
9d72cede 211 ),
51fa20cb 212 ),
a1a2a83d 213 'receive_date' => array('operatorType' => CRM_Report_Form::OP_DATE),
db96d968 214 'contribution_source' => array(
215 'title' => ts('Source'),
216 'name' => 'source',
217 'type' => CRM_Utils_Type::T_STRING,
218 ),
a1a2a83d 219 'currency' => array(
fd6a6828 220 'title' => ts('Currency'),
a1a2a83d
TO
221 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
222 'options' => CRM_Core_OptionGroup::values('currencies_enabled'),
223 'default' => NULL,
224 'type' => CRM_Utils_Type::T_STRING,
225 ),
226 'financial_type_id' => array(
227 'title' => ts('Financial Type'),
228 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
4f166b15 229 'options' => CRM_Financial_BAO_FinancialType::getAvailableFinancialTypes(),
a1a2a83d
TO
230 'type' => CRM_Utils_Type::T_INT,
231 ),
232 'contribution_page_id' => array(
233 'title' => ts('Contribution Page'),
234 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
235 'options' => CRM_Contribute_PseudoConstant::contributionPage(),
236 'type' => CRM_Utils_Type::T_INT,
237 ),
238 'payment_instrument_id' => array(
239 'title' => ts('Payment Type'),
240 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
241 'options' => CRM_Contribute_PseudoConstant::paymentInstrument(),
242 'type' => CRM_Utils_Type::T_INT,
243 ),
244 'contribution_status_id' => array(
245 'title' => ts('Contribution Status'),
246 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
247 'options' => CRM_Contribute_PseudoConstant::contributionStatus(),
248 'default' => array(1),
249 'type' => CRM_Utils_Type::T_INT,
250 ),
251 'total_amount' => array('title' => ts('Contribution Amount')),
51fa20cb 252 ),
a1a2a83d
TO
253 'order_bys' => array(
254 'financial_type_id' => array('title' => ts('Financial Type')),
255 'contribution_status_id' => array('title' => ts('Contribution Status')),
536f0e02 256 'payment_instrument_id' => array('title' => ts('Payment Method')),
7bc6b5bb 257 'receive_date' => array('title' => ts('Date Received')),
a1a2a83d
TO
258 ),
259 'grouping' => 'contri-fields',
260 ),
261 'civicrm_contribution_soft' => array(
262 'dao' => 'CRM_Contribute_DAO_ContributionSoft',
263 'fields' => array(
264 'soft_credit_type_id' => array('title' => ts('Soft Credit Type')),
265 ),
266 'filters' => array(
267 'soft_credit_type_id' => array(
fd6a6828 268 'title' => ts('Soft Credit Type'),
a1a2a83d
TO
269 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
270 'options' => CRM_Core_OptionGroup::values('soft_credit_type'),
271 'default' => NULL,
272 'type' => CRM_Utils_Type::T_STRING,
6a488035
TO
273 ),
274 ),
a1a2a83d 275 ),
433465bc
PN
276 'civicrm_batch' => array(
277 'dao' => 'CRM_Batch_DAO_EntityBatch',
278 'grouping' => 'contri-fields',
279 'fields' => array(
280 'batch_id' => array(
49d516c6 281 'name' => 'batch_id',
433465bc
PN
282 'title' => ts('Batch Name'),
283 ),
284 ),
285 'filters' => array(
286 'bid' => array(
287 'title' => ts('Batch Name'),
288 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
289 'options' => CRM_Batch_BAO_Batch::getBatches(),
290 'type' => CRM_Utils_Type::T_INT,
291 'dbAlias' => 'batch_civireport.batch_id',
292 ),
293 ),
294 ),
a1a2a83d
TO
295 'civicrm_contribution_ordinality' => array(
296 'dao' => 'CRM_Contribute_DAO_Contribution',
297 'alias' => 'cordinality',
298 'filters' => array(
299 'ordinality' => array(
300 'title' => ts('Contribution Ordinality'),
301 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
302 'options' => array(
303 0 => 'First by Contributor',
304 1 => 'Second or Later by Contributor',
305 ),
306 'type' => CRM_Utils_Type::T_INT,
6a488035 307 ),
a1a2a83d
TO
308 ),
309 ),
310 'civicrm_note' => array(
311 'dao' => 'CRM_Core_DAO_Note',
312 'fields' => array(
313 'contribution_note' => array(
314 'name' => 'note',
315 'title' => ts('Contribution Note'),
316 ),
317 ),
318 'filters' => array(
319 'note' => array(
320 'name' => 'note',
321 'title' => ts('Contribution Note'),
322 'operator' => 'like',
323 'type' => CRM_Utils_Type::T_STRING,
6a488035
TO
324 ),
325 ),
a1a2a83d
TO
326 ),
327 ) + $this->addAddressFields(FALSE);
a703d90c 328 // The tests test for this variation of the sort_name field. Don't argue with the tests :-).
25cf1ac5 329 $this->_columns['civicrm_contact']['fields']['sort_name']['title'] = ts('Donor Name');
16e2e80c 330 $this->_groupFilter = TRUE;
6a488035 331 $this->_tagFilter = TRUE;
1c4d8c3e 332
9d72cede 333 // If we have active campaigns add those elements to both the fields and filters
6a488035
TO
334 if ($campaignEnabled && !empty($this->activeCampaigns)) {
335 $this->_columns['civicrm_contribution']['fields']['campaign_id'] = array(
336 'title' => ts('Campaign'),
337 'default' => 'false',
338 );
9d72cede
EM
339 $this->_columns['civicrm_contribution']['filters']['campaign_id'] = array(
340 'title' => ts('Campaign'),
6a488035
TO
341 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
342 'options' => $this->activeCampaigns,
942b1d01 343 'type' => CRM_Utils_Type::T_INT,
6a488035
TO
344 );
345 $this->_columns['civicrm_contribution']['order_bys']['campaign_id'] = array('title' => ts('Campaign'));
346 }
7a961f19 347
348 $this->_currencyColumn = 'civicrm_contribution_currency';
6a488035
TO
349 parent::__construct();
350 }
351
00be9182 352 public function preProcess() {
6a488035
TO
353 parent::preProcess();
354 }
355
00be9182 356 public function select() {
6a488035 357 $this->_columnHeaders = array();
6a488035 358
f2947aea 359 parent::select();
8e3ad5e0
WA
360 //total_amount was affected by sum as it is considered as one of the stat field
361 //so it is been replaced with correct alias, CRM-13833
362 $this->_select = str_replace("sum({$this->_aliases['civicrm_contribution']}.total_amount)", "{$this->_aliases['civicrm_contribution']}.total_amount", $this->_select);
d1641c51 363 $this->_selectClauses = str_replace("sum({$this->_aliases['civicrm_contribution']}.total_amount)", "{$this->_aliases['civicrm_contribution']}.total_amount", $this->_selectClauses);
f2947aea
DS
364 }
365
00be9182 366 public function orderBy() {
f2947aea
DS
367 parent::orderBy();
368
369 // please note this will just add the order-by columns to select query, and not display in column-headers.
370 // This is a solution to not throw fatal errors when there is a column in order-by, not present in select/display columns.
371 foreach ($this->_orderByFields as $orderBy) {
9d72cede 372 if (!array_key_exists($orderBy['name'], $this->_params['fields']) &&
263b89e5 373 empty($orderBy['section']) && (strpos($this->_select, $orderBy['dbAlias']) === FALSE)
9d72cede 374 ) {
f2947aea
DS
375 $this->_select .= ", {$orderBy['dbAlias']} as {$orderBy['tplField']}";
376 }
377 }
6a488035
TO
378 }
379
74cf4551 380 /**
8b7f2513 381 * Set the FROM clause for the report.
74cf4551 382 */
8b7f2513 383 public function from() {
6a488035
TO
384 $this->_from = "
385 FROM civicrm_contact {$this->_aliases['civicrm_contact']} {$this->_aclFrom}
1c4d8c3e 386 INNER JOIN civicrm_contribution {$this->_aliases['civicrm_contribution']}
6c7de867 387 ON {$this->_aliases['civicrm_contact']}.id = {$this->_aliases['civicrm_contribution']}.contact_id AND {$this->_aliases['civicrm_contribution']}.is_test = 0";
40c655aa 388
9d72cede
EM
389 if (CRM_Utils_Array::value('contribution_or_soft_value', $this->_params) ==
390 'both'
391 ) {
51fa20cb 392 $this->_from .= "\n LEFT JOIN civicrm_contribution_soft contribution_soft_civireport
393 ON contribution_soft_civireport.contribution_id = {$this->_aliases['civicrm_contribution']}.id";
394 }
9d72cede
EM
395 elseif (CRM_Utils_Array::value('contribution_or_soft_value', $this->_params) ==
396 'soft_credits_only'
397 ) {
51fa20cb 398 $this->_from .= "\n INNER JOIN civicrm_contribution_soft contribution_soft_civireport
399 ON contribution_soft_civireport.contribution_id = {$this->_aliases['civicrm_contribution']}.id";
c548e07b 400 }
8b7f2513 401 $this->appendAdditionalFromJoins();
6a488035
TO
402 }
403
00be9182 404 public function groupBy() {
d1641c51 405 $groupBy = array("{$this->_aliases['civicrm_contact']}.id", "{$this->_aliases['civicrm_contribution']}.id");
b708c08d 406 $this->_groupBy = CRM_Contact_BAO_Query::getGroupByFromSelectColumns($this->_selectClauses, $groupBy);
6a488035
TO
407 }
408
74cf4551
EM
409 /**
410 * @param $rows
411 *
412 * @return array
413 */
00be9182 414 public function statistics(&$rows) {
6a488035
TO
415 $statistics = parent::statistics($rows);
416
7010bab9 417 $totalAmount = $average = $fees = $net = array();
7a961f19 418 $count = 0;
6a488035
TO
419 $select = "
420 SELECT COUNT({$this->_aliases['civicrm_contribution']}.total_amount ) as count,
421 SUM( {$this->_aliases['civicrm_contribution']}.total_amount ) as amount,
7a961f19 422 ROUND(AVG({$this->_aliases['civicrm_contribution']}.total_amount), 2) as avg,
7010bab9 423 {$this->_aliases['civicrm_contribution']}.currency as currency,
e468f7a2 424 SUM( {$this->_aliases['civicrm_contribution']}.fee_amount ) as fees,
7010bab9 425 SUM( {$this->_aliases['civicrm_contribution']}.net_amount ) as net
6a488035
TO
426 ";
427
7a961f19 428 $group = "\nGROUP BY {$this->_aliases['civicrm_contribution']}.currency";
429 $sql = "{$select} {$this->_from} {$this->_where} {$group}";
6a488035
TO
430 $dao = CRM_Core_DAO::executeQuery($sql);
431
7a961f19 432 while ($dao->fetch()) {
7010bab9
DG
433 $totalAmount[] = CRM_Utils_Money::format($dao->amount, $dao->currency) . " (" . $dao->count . ")";
434 $fees[] = CRM_Utils_Money::format($dao->fees, $dao->currency);
435 $net[] = CRM_Utils_Money::format($dao->net, $dao->currency);
9d72cede 436 $average[] = CRM_Utils_Money::format($dao->avg, $dao->currency);
1c4d8c3e 437 $count += $dao->count;
6a488035 438 }
7a961f19 439 $statistics['counts']['amount'] = array(
be205937 440 'title' => ts('Total Amount (Contributions)'),
7a961f19 441 'value' => implode(', ', $totalAmount),
442 'type' => CRM_Utils_Type::T_STRING,
443 );
444 $statistics['counts']['count'] = array(
be205937 445 'title' => ts('Total Contributions'),
7a961f19 446 'value' => $count,
447 );
7010bab9
DG
448 $statistics['counts']['fees'] = array(
449 'title' => ts('Fees'),
450 'value' => implode(', ', $fees),
451 'type' => CRM_Utils_Type::T_STRING,
452 );
453 $statistics['counts']['net'] = array(
454 'title' => ts('Net'),
455 'value' => implode(', ', $net),
456 'type' => CRM_Utils_Type::T_STRING,
457 );
7a961f19 458 $statistics['counts']['avg'] = array(
459 'title' => ts('Average'),
460 'value' => implode(', ', $average),
461 'type' => CRM_Utils_Type::T_STRING,
462 );
6a488035 463
70c41bce 464 // Stats for soft credits
9d72cede
EM
465 if ($this->_softFrom &&
466 CRM_Utils_Array::value('contribution_or_soft_value', $this->_params) !=
467 'contributions_only'
468 ) {
c548e07b 469 $totalAmount = $average = array();
9d72cede 470 $count = 0;
c548e07b 471 $select = "
70c41bce
DS
472SELECT COUNT(contribution_soft_civireport.amount ) as count,
473 SUM(contribution_soft_civireport.amount ) as amount,
474 ROUND(AVG(contribution_soft_civireport.amount), 2) as avg,
475 {$this->_aliases['civicrm_contribution']}.currency as currency";
c548e07b 476 $sql = "
f813f78e 477{$select}
478{$this->_softFrom}
c548e07b
DS
479GROUP BY {$this->_aliases['civicrm_contribution']}.currency";
480 $dao = CRM_Core_DAO::executeQuery($sql);
481 while ($dao->fetch()) {
6c552737 482 $totalAmount[] = CRM_Utils_Money::format($dao->amount, $dao->currency) . " (" .
9d72cede
EM
483 $dao->count . ")";
484 $average[] = CRM_Utils_Money::format($dao->avg, $dao->currency);
c548e07b
DS
485 $count += $dao->count;
486 }
487 $statistics['counts']['softamount'] = array(
488 'title' => ts('Total Amount (Soft Credits)'),
489 'value' => implode(', ', $totalAmount),
490 'type' => CRM_Utils_Type::T_STRING,
491 );
492 $statistics['counts']['softcount'] = array(
493 'title' => ts('Total Soft Credits'),
494 'value' => $count,
495 );
496 $statistics['counts']['softavg'] = array(
497 'title' => ts('Average (Soft Credits)'),
498 'value' => implode(', ', $average),
499 'type' => CRM_Utils_Type::T_STRING,
500 );
70c41bce 501 }
70c41bce 502
6a488035
TO
503 return $statistics;
504 }
505
8b7f2513 506 /**
507 * This function appears to have been overrriden for the purposes of facilitating soft credits in the report.
508 *
509 * An alternative approach would have been to have had 2 reports.
510 * 1) contribution report with optional join extending the retrievable fields & filters with soft credit data
511 * 2) soft credit report - showing a row per 'payment engagement' (payment or soft credit).
512 *
513 * As it is many people are confused by the duplicate rows in 'soft credit mode' and this report is complex
514 * and slowed down by soft credit calculations regardless of whether that information is desired.
515 *
516 * Soft credit functionality is not currently unit tested for this report.
517 */
00be9182 518 public function postProcess() {
6a488035
TO
519 // get the acl clauses built before we assemble the query
520 $this->buildACLClause($this->_aliases['civicrm_contact']);
70c41bce
DS
521
522 $this->beginPostProcess();
e468f7a2 523 // CRM-18312 - display soft_credits and soft_credits_for column
524 // when 'Contribution or Soft Credit?' column is not selected
525 if (empty($this->_params['fields']['contribution_or_soft'])) {
526 $this->_params['fields']['contribution_or_soft'] = 1;
527 $this->noDisplayContributionOrSoftColumn = TRUE;
528 }
f813f78e 529
9d72cede
EM
530 if (CRM_Utils_Array::value('contribution_or_soft_value', $this->_params) ==
531 'contributions_only' &&
532 !empty($this->_params['fields']['soft_credit_type_id'])
533 ) {
51fa20cb 534 unset($this->_params['fields']['soft_credit_type_id']);
535 if (!empty($this->_params['soft_credit_type_id_value'])) {
536 $this->_params['soft_credit_type_id_value'] = array();
537 }
538 }
539
70c41bce
DS
540 // 1. use main contribution query to build temp table 1
541 $sql = $this->buildQuery();
6c552737 542 $tempQuery = 'CREATE TEMPORARY TABLE civireport_contribution_detail_temp1 AS ' . $sql;
70c41bce
DS
543 CRM_Core_DAO::executeQuery($tempQuery);
544 $this->setPager();
545
c548e07b 546 // 2. customize main contribution query for soft credit, and build temp table 2 with soft credit contributions only
8b7f2513 547 $this->softCreditFrom();
be78fc2d
DL
548 // also include custom group from if included
549 // since this might be included in select
550 $this->customDataFrom();
551
70c41bce
DS
552 $select = str_ireplace('contribution_civireport.total_amount', 'contribution_soft_civireport.amount', $this->_select);
553 $select = str_ireplace("'Contribution' as", "'Soft Credit' as", $select);
8b7f2513 554 // We really don't want to join soft credit in if not required.
555 if (!empty($this->_groupBy) && !$this->noDisplayContributionOrSoftColumn) {
3636b520 556 $this->_groupBy .= ', contribution_soft_civireport.amount';
557 }
70c41bce 558 // we inner join with temp1 to restrict soft contributions to those in temp1 table
51fa20cb 559 $sql = "{$select} {$this->_from} {$this->_where} {$this->_groupBy}";
6c552737 560 $tempQuery = 'CREATE TEMPORARY TABLE civireport_contribution_detail_temp2 AS ' . $sql;
70c41bce 561 CRM_Core_DAO::executeQuery($tempQuery);
9d72cede
EM
562 if (CRM_Utils_Array::value('contribution_or_soft_value', $this->_params) ==
563 'soft_credits_only'
564 ) {
c548e07b
DS
565 // revise pager : prev, next based on soft-credits only
566 $this->setPager();
f813f78e 567 }
70c41bce
DS
568
569 // copy _from for later use of stats calculation for soft credits, and reset $this->_from to main query
570 $this->_softFrom = $this->_from;
c93f6d83
DL
571
572 // simple reset of ->_from
573 $this->from();
be78fc2d
DL
574
575 // also include custom group from if included
576 // since this might be included in select
c93f6d83 577 $this->customDataFrom();
70c41bce 578
c548e07b 579 // 3. Decide where to populate temp3 table from
9d72cede
EM
580 if (CRM_Utils_Array::value('contribution_or_soft_value', $this->_params) ==
581 'contributions_only'
582 ) {
1ebef749 583 $tempQuery = "(SELECT * FROM civireport_contribution_detail_temp1)";
9d72cede 584 }
4c9b6178 585 elseif (CRM_Utils_Array::value('contribution_or_soft_value', $this->_params) ==
9d72cede
EM
586 'soft_credits_only'
587 ) {
1ebef749 588 $tempQuery = "(SELECT * FROM civireport_contribution_detail_temp2)";
9d72cede
EM
589 }
590 else {
c548e07b 591 $tempQuery = "
1ebef749 592(SELECT * FROM civireport_contribution_detail_temp1)
c548e07b 593UNION ALL
1ebef749 594(SELECT * FROM civireport_contribution_detail_temp2)";
c548e07b
DS
595 }
596
597 // 4. build temp table 3
1ebef749
DS
598 $sql = "CREATE TEMPORARY TABLE civireport_contribution_detail_temp3 AS {$tempQuery}";
599 CRM_Core_DAO::executeQuery($sql);
600
601 // 5. Re-construct order-by to make sense for final query on temp3 table
602 $orderBy = '';
603 if (!empty($this->_orderByArray)) {
604 $aliases = array_flip($this->_aliases);
605 $orderClause = array();
606 foreach ($this->_orderByArray as $clause) {
607 list($alias, $rest) = explode('.', $clause);
263b89e5 608 // CRM-17280 -- In case, we are ordering by custom fields
609 // modify $rest to match the alias used for them in temp3 table
610 $grp = new CRM_Core_DAO_CustomGroup();
611 $grp->table_name = $aliases[$alias];
612 if ($grp->find()) {
613 list($fld, $order) = explode(' ', $rest);
ba5db1c7 614 foreach ($this->_columns[$aliases[$alias]]['fields'] as $fldName => $value) {
263b89e5 615 if ($value['name'] == $fld) {
616 $fld = $fldName;
617 }
618 }
44fc78ec 619 $rest = "{$fld} {$order}";
263b89e5 620 }
1ebef749
DS
621 $orderClause[] = $aliases[$alias] . "_" . $rest;
622 }
84178120 623 $orderBy = (!empty($orderClause)) ? "ORDER BY " . implode(', ', $orderClause) : '';
70c41bce
DS
624 }
625
1ebef749 626 // 6. show result set from temp table 3
70c41bce 627 $rows = array();
9d72cede 628 $sql = "SELECT * FROM civireport_contribution_detail_temp3 {$orderBy}";
70c41bce
DS
629 $this->buildRows($sql, $rows);
630
631 // format result set.
632 $this->formatDisplay($rows, FALSE);
633
634 // assign variables to templates
635 $this->doTemplateAssignment($rows);
70c41bce
DS
636 // do print / pdf / instance stuff if needed
637 $this->endPostProcess($rows);
6a488035
TO
638 }
639
74cf4551 640 /**
ced9bfed
EM
641 * Alter display of rows.
642 *
643 * Iterate through the rows retrieved via SQL and make changes for display purposes,
644 * such as rendering contacts as links.
645 *
646 * @param array $rows
647 * Rows generated by SQL, with an array for each row.
74cf4551 648 */
00be9182 649 public function alterDisplay(&$rows) {
9d72cede
EM
650 $checkList = array();
651 $entryFound = FALSE;
652 $display_flag = $prev_cid = $cid = 0;
653 $contributionTypes = CRM_Contribute_PseudoConstant::financialType();
6a488035
TO
654 $contributionStatus = CRM_Contribute_PseudoConstant::contributionStatus();
655 $paymentInstruments = CRM_Contribute_PseudoConstant::paymentInstrument();
9d72cede 656 $contributionPages = CRM_Contribute_PseudoConstant::contributionPage();
433465bc 657 $batches = CRM_Batch_BAO_Batch::getBatches();
6a488035
TO
658 foreach ($rows as $rowNum => $row) {
659 if (!empty($this->_noRepeats) && $this->_outputMode != 'csv') {
660 // don't repeat contact details if its same as the previous row
661 if (array_key_exists('civicrm_contact_id', $row)) {
662 if ($cid = $row['civicrm_contact_id']) {
663 if ($rowNum == 0) {
664 $prev_cid = $cid;
665 }
666 else {
667 if ($prev_cid == $cid) {
668 $display_flag = 1;
669 $prev_cid = $cid;
670 }
671 else {
672 $display_flag = 0;
673 $prev_cid = $cid;
674 }
675 }
676
677 if ($display_flag) {
678 foreach ($row as $colName => $colVal) {
679 // Hide repeats in no-repeat columns, but not if the field's a section header
9d72cede
EM
680 if (in_array($colName, $this->_noRepeats) &&
681 !array_key_exists($colName, $this->_sections)
682 ) {
6a488035
TO
683 unset($rows[$rowNum][$colName]);
684 }
685 }
686 }
687 $entryFound = TRUE;
688 }
689 }
690 }
691
9d72cede
EM
692 if (CRM_Utils_Array::value('civicrm_contribution_contribution_or_soft', $rows[$rowNum]) ==
693 'Contribution'
694 ) {
8e3ad5e0
WA
695 unset($rows[$rowNum]['civicrm_contribution_soft_soft_credit_type_id']);
696 }
6a488035 697
a703d90c 698 $entryFound = $this->alterDisplayContactFields($row, $rows, $rowNum, 'contribution/detail', ts('View Contribution Details')) ? TRUE : $entryFound;
6a488035 699 // convert donor sort name to link
9d72cede
EM
700 if (array_key_exists('civicrm_contact_sort_name', $row) &&
701 !empty($rows[$rowNum]['civicrm_contact_sort_name']) &&
6a488035
TO
702 array_key_exists('civicrm_contact_id', $row)
703 ) {
704 $url = CRM_Utils_System::url("civicrm/contact/view",
705 'reset=1&cid=' . $row['civicrm_contact_id'],
706 $this->_absoluteUrl
707 );
708 $rows[$rowNum]['civicrm_contact_sort_name_link'] = $url;
709 $rows[$rowNum]['civicrm_contact_sort_name_hover'] = ts("View Contact Summary for this Contact.");
710 }
711
6a488035
TO
712 if ($value = CRM_Utils_Array::value('civicrm_contribution_financial_type_id', $row)) {
713 $rows[$rowNum]['civicrm_contribution_financial_type_id'] = $contributionTypes[$value];
714 $entryFound = TRUE;
715 }
716 if ($value = CRM_Utils_Array::value('civicrm_contribution_contribution_status_id', $row)) {
717 $rows[$rowNum]['civicrm_contribution_contribution_status_id'] = $contributionStatus[$value];
718 $entryFound = TRUE;
719 }
0d0e945a
DG
720 if ($value = CRM_Utils_Array::value('civicrm_contribution_contribution_page_id', $row)) {
721 $rows[$rowNum]['civicrm_contribution_contribution_page_id'] = $contributionPages[$value];
722 $entryFound = TRUE;
723 }
6a488035
TO
724 if ($value = CRM_Utils_Array::value('civicrm_contribution_payment_instrument_id', $row)) {
725 $rows[$rowNum]['civicrm_contribution_payment_instrument_id'] = $paymentInstruments[$value];
726 $entryFound = TRUE;
727 }
433465bc
PN
728 if (!empty($row['civicrm_batch_batch_id'])) {
729 $rows[$rowNum]['civicrm_batch_batch_id'] = CRM_Utils_Array::value($row['civicrm_batch_batch_id'], $batches);
6a488035
TO
730 $entryFound = TRUE;
731 }
732
733 // Contribution amount links to viewing contribution
734 if (($value = CRM_Utils_Array::value('civicrm_contribution_total_amount_sum', $row)) &&
735 CRM_Core_Permission::check('access CiviContribute')
736 ) {
737 $url = CRM_Utils_System::url("civicrm/contact/view/contribution",
9d72cede
EM
738 "reset=1&id=" . $row['civicrm_contribution_contribution_id'] .
739 "&cid=" . $row['civicrm_contact_id'] .
740 "&action=view&context=contribution&selectedChild=contribute",
6a488035
TO
741 $this->_absoluteUrl
742 );
743 $rows[$rowNum]['civicrm_contribution_total_amount_sum_link'] = $url;
744 $rows[$rowNum]['civicrm_contribution_total_amount_sum_hover'] = ts("View Details of this Contribution.");
745 $entryFound = TRUE;
746 }
747
748 // convert campaign_id to campaign title
749 if (array_key_exists('civicrm_contribution_campaign_id', $row)) {
750 if ($value = $row['civicrm_contribution_campaign_id']) {
751 $rows[$rowNum]['civicrm_contribution_campaign_id'] = $this->activeCampaigns[$value];
752 $entryFound = TRUE;
753 }
754 }
755
70c41bce
DS
756 // soft credits
757 if (array_key_exists('civicrm_contribution_soft_credits', $row) &&
9d72cede
EM
758 'Contribution' ==
759 CRM_Utils_Array::value('civicrm_contribution_contribution_or_soft', $rows[$rowNum]) &&
70c41bce
DS
760 array_key_exists('civicrm_contribution_contribution_id', $row)
761 ) {
762 $query = "
f813f78e 763SELECT civicrm_contact_id, civicrm_contact_sort_name, civicrm_contribution_total_amount_sum, civicrm_contribution_currency
70c41bce
DS
764FROM civireport_contribution_detail_temp2
765WHERE civicrm_contribution_contribution_id={$row['civicrm_contribution_contribution_id']}";
9d72cede 766 $dao = CRM_Core_DAO::executeQuery($query);
70c41bce 767 $string = '';
1ebef749 768 $separator = ($this->_outputMode !== 'csv') ? "<br/>" : ' ';
70c41bce 769 while ($dao->fetch()) {
9d72cede
EM
770 $url = CRM_Utils_System::url("civicrm/contact/view", 'reset=1&cid=' .
771 $dao->civicrm_contact_id);
772 $string = $string . ($string ? $separator : '') .
773 "<a href='{$url}'>{$dao->civicrm_contact_sort_name}</a> " .
c548e07b 774 CRM_Utils_Money::format($dao->civicrm_contribution_total_amount_sum, $dao->civicrm_contribution_currency);
70c41bce
DS
775 }
776 $rows[$rowNum]['civicrm_contribution_soft_credits'] = $string;
777 }
778
779 if (array_key_exists('civicrm_contribution_soft_credit_for', $row) &&
9d72cede
EM
780 'Soft Credit' ==
781 CRM_Utils_Array::value('civicrm_contribution_contribution_or_soft', $rows[$rowNum]) &&
70c41bce
DS
782 array_key_exists('civicrm_contribution_contribution_id', $row)
783 ) {
784 $query = "
f813f78e 785SELECT civicrm_contact_id, civicrm_contact_sort_name
70c41bce
DS
786FROM civireport_contribution_detail_temp1
787WHERE civicrm_contribution_contribution_id={$row['civicrm_contribution_contribution_id']}";
9d72cede 788 $dao = CRM_Core_DAO::executeQuery($query);
70c41bce
DS
789 $string = '';
790 while ($dao->fetch()) {
9d72cede
EM
791 $url = CRM_Utils_System::url("civicrm/contact/view", 'reset=1&cid=' .
792 $dao->civicrm_contact_id);
793 $string = $string .
794 "\n<a href='{$url}'>{$dao->civicrm_contact_sort_name}</a>";
70c41bce
DS
795 }
796 $rows[$rowNum]['civicrm_contribution_soft_credit_for'] = $string;
797 }
798
e468f7a2 799 // CRM-18312 - hide 'contribution_or_soft' column if unchecked.
800 if (!empty($this->noDisplayContributionOrSoftColumn)) {
801 unset($rows[$rowNum]['civicrm_contribution_contribution_or_soft']);
802 unset($this->_columnHeaders['civicrm_contribution_contribution_or_soft']);
803 }
804
51fa20cb 805 //convert soft_credit_type_id into label
806 if (array_key_exists('civicrm_contribution_soft_soft_credit_type_id', $rows[$rowNum])) {
7a4192f7 807 $rows[$rowNum]['civicrm_contribution_soft_soft_credit_type_id'] = CRM_Core_PseudoConstant::getLabel(
808 'CRM_Contribute_BAO_ContributionSoft',
809 'soft_credit_type_id',
810 $row['civicrm_contribution_soft_soft_credit_type_id']
811 );
51fa20cb 812 }
813
6a488035
TO
814 $entryFound = $this->alterDisplayAddressFields($row, $rows, $rowNum, 'contribute/detail', 'List all contribution(s) for this ') ? TRUE : $entryFound;
815
816 // skip looking further in rows, if first row itself doesn't
817 // have the column we need
818 if (!$entryFound) {
819 break;
820 }
821 $lastKey = $rowNum;
822 }
823 }
1c4d8c3e 824
00be9182 825 public function sectionTotals() {
6a488035
TO
826
827 // Reports using order_bys with sections must populate $this->_selectAliases in select() method.
828 if (empty($this->_selectAliases)) {
829 return;
830 }
831
832 if (!empty($this->_sections)) {
833 // build the query with no LIMIT clause
9d72cede 834 $select = str_ireplace('SELECT SQL_CALC_FOUND_ROWS ', 'SELECT ', $this->_select);
6a488035
TO
835 $sql = "{$select} {$this->_from} {$this->_where} {$this->_groupBy} {$this->_having} {$this->_orderBy}";
836
837 // pull section aliases out of $this->_sections
838 $sectionAliases = array_keys($this->_sections);
839
840 $ifnulls = array();
841 foreach (array_merge($sectionAliases, $this->_selectAliases) as $alias) {
842 $ifnulls[] = "ifnull($alias, '') as $alias";
843 }
b708c08d 844 $this->_select = "SELECT " . implode(", ", $ifnulls);
36d2f4d5 845 $this->_select = CRM_Contact_BAO_Query::appendAnyValueToSelect($ifnulls, $sectionAliases);
6a488035
TO
846
847 /* Group (un-limited) report by all aliases and get counts. This might
e70a7fc0
TO
848 * be done more efficiently when the contents of $sql are known, ie. by
849 * overriding this method in the report class.
850 */
6a488035
TO
851
852 $addtotals = '';
853
9d72cede
EM
854 if (array_search("civicrm_contribution_total_amount_sum", $this->_selectAliases) !==
855 FALSE
856 ) {
6a488035
TO
857 $addtotals = ", sum(civicrm_contribution_total_amount_sum) as sumcontribs";
858 $showsumcontribs = TRUE;
859 }
860
b708c08d 861 $query = $this->_select .
9d72cede
EM
862 "$addtotals, count(*) as ct from civireport_contribution_detail_temp3 group by " .
863 implode(", ", $sectionAliases);
6a488035
TO
864 // initialize array of total counts
865 $sumcontribs = $totals = array();
866 $dao = CRM_Core_DAO::executeQuery($query);
867 while ($dao->fetch()) {
868
869 // let $this->_alterDisplay translate any integer ids to human-readable values.
870 $rows[0] = $dao->toArray();
871 $this->alterDisplay($rows);
872 $row = $rows[0];
873
874 // add totals for all permutations of section values
875 $values = array();
876 $i = 1;
877 $aliasCount = count($sectionAliases);
878 foreach ($sectionAliases as $alias) {
879 $values[] = $row[$alias];
880 $key = implode(CRM_Core_DAO::VALUE_SEPARATOR, $values);
881 if ($i == $aliasCount) {
882 // the last alias is the lowest-level section header; use count as-is
883 $totals[$key] = $dao->ct;
9d72cede
EM
884 if ($showsumcontribs) {
885 $sumcontribs[$key] = $dao->sumcontribs;
886 }
1c4d8c3e 887 }
6a488035
TO
888 else {
889 // other aliases are higher level; roll count into their total
84178120 890 $totals[$key] = (array_key_exists($key, $totals)) ? $totals[$key] + $dao->ct : $dao->ct;
1c4d8c3e 891 if ($showsumcontribs) {
84178120 892 $sumcontribs[$key] = array_key_exists($key, $sumcontribs) ? $sumcontribs[$key] + $dao->sumcontribs : $dao->sumcontribs;
6a488035
TO
893 }
894 }
895 }
896 }
897 if ($showsumcontribs) {
898 $totalandsum = array();
0161a899
ML
899 // ts exception to avoid having ts("%1 %2: %3")
900 $title = '%1 contributions / soft-credits: %2';
901
9d72cede
EM
902 if (CRM_Utils_Array::value('contribution_or_soft_value', $this->_params) ==
903 'contributions_only'
904 ) {
0161a899 905 $title = '%1 contributions: %2';
9d72cede 906 }
4c9b6178 907 elseif (CRM_Utils_Array::value('contribution_or_soft_value', $this->_params) ==
9d72cede
EM
908 'soft_credits_only'
909 ) {
0161a899 910 $title = '%1 soft-credits: %2';
bec9ef32 911 }
6a488035 912 foreach ($totals as $key => $total) {
0161a899 913 $totalandsum[$key] = ts($title, array(
1c4d8c3e 914 1 => $total,
21dfd5f5 915 2 => CRM_Utils_Money::format($sumcontribs[$key]),
10a5be27 916 ));
6a488035
TO
917 }
918 $this->assign('sectionTotals', $totalandsum);
919 }
920 else {
921 $this->assign('sectionTotals', $totals);
922 }
923 }
924 }
96025800 925
8b7f2513 926 /**
927 * Generate the from clause as it relates to the soft credits.
928 */
929 public function softCreditFrom() {
930
931 $this->_from = "
932 FROM civireport_contribution_detail_temp1 temp1_civireport
933 INNER JOIN civicrm_contribution {$this->_aliases['civicrm_contribution']}
934 ON temp1_civireport.civicrm_contribution_contribution_id = {$this->_aliases['civicrm_contribution']}.id
935 INNER JOIN civicrm_contribution_soft contribution_soft_civireport
936 ON contribution_soft_civireport.contribution_id = {$this->_aliases['civicrm_contribution']}.id
937 INNER JOIN civicrm_contact {$this->_aliases['civicrm_contact']}
938 ON {$this->_aliases['civicrm_contact']}.id = contribution_soft_civireport.contact_id
939 {$this->_aclFrom}";
940
941 $this->appendAdditionalFromJoins();
942 }
943
944 /**
945 * Append the joins that are required regardless of context.
946 */
947 public function appendAdditionalFromJoins() {
948 if (!empty($this->_params['ordinality_value'])) {
949 $this->_from .= "
950 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']}
951 ON {$this->_aliases['civicrm_contribution_ordinality']}.id = {$this->_aliases['civicrm_contribution']}.id";
952 }
953 $this->addPhoneFromClause();
954
955 $this->addAddressFromClause();
956
957 if ($this->_emailField) {
958 $this->_from .= "
959 LEFT JOIN civicrm_email {$this->_aliases['civicrm_email']}
960 ON {$this->_aliases['civicrm_contact']}.id = {$this->_aliases['civicrm_email']}.contact_id AND
961 {$this->_aliases['civicrm_email']}.is_primary = 1\n";
962 }
963 // include contribution note
964 if (!empty($this->_params['fields']['contribution_note']) ||
965 !empty($this->_params['note_value'])
966 ) {
967 $this->_from .= "
968 LEFT JOIN civicrm_note {$this->_aliases['civicrm_note']}
969 ON ( {$this->_aliases['civicrm_note']}.entity_table = 'civicrm_contribution' AND
970 {$this->_aliases['civicrm_contribution']}.id = {$this->_aliases['civicrm_note']}.entity_id )";
971 }
972 //for contribution batches
973 if (!empty($this->_params['fields']['batch_id']) ||
974 !empty($this->_params['bid_value'])
975 ) {
976 $this->_from .= "
977 LEFT JOIN civicrm_entity_financial_trxn eft
978 ON eft.entity_id = {$this->_aliases['civicrm_contribution']}.id AND
979 eft.entity_table = 'civicrm_contribution'
980 LEFT JOIN civicrm_entity_batch {$this->_aliases['civicrm_batch']}
981 ON ({$this->_aliases['civicrm_batch']}.entity_id = eft.financial_trxn_id
982 AND {$this->_aliases['civicrm_batch']}.entity_table = 'civicrm_financial_trxn')";
983 }
984 }
985
6a488035 986}