reporting-20: don't display soft credits by default when force=1
[civicrm-core.git] / CRM / Report / Form / Contribute / Detail.php
CommitLineData
6a488035 1<?php
6a488035
TO
2/*
3 +--------------------------------------------------------------------+
fee14197 4 | CiviCRM version 5 |
6a488035 5 +--------------------------------------------------------------------+
6b83d5bd 6 | Copyright CiviCRM LLC (c) 2004-2019 |
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
6b83d5bd 31 * @copyright CiviCRM LLC (c) 2004-2019
6a488035
TO
32 */
33class CRM_Report_Form_Contribute_Detail extends CRM_Report_Form {
6a488035
TO
34
35 protected $_summary = NULL;
36
e9b25936
E
37 protected $_softFrom = NULL;
38
f568f786 39 protected $noDisplayContributionOrSoftColumn = FALSE;
40
be2fb01f 41 protected $_customGroupExtends = [
70bea8e2 42 'Contact',
43 'Individual',
7d793900 44 'Contribution',
be2fb01f 45 ];
6a488035 46
81a22d3d 47 protected $groupConcatTested = TRUE;
48
e6bab5ea 49 protected $isTempTableBuilt = FALSE;
50
51 /**
52 * Query mode.
53 *
54 * This can be 'Main' or 'SoftCredit' to denote which query we are building.
55 *
56 * @var string
57 */
58 protected $queryMode = 'Main';
59
60 /**
61 * Is this report being run on contributions as the base entity.
62 *
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
65 * of complexity.
66 *
67 * If it is in isContributionsOnlyMode we can simplify.
68 *
69 * (arguably there should be 2 separate report templates, not one doing double duty.)
70 *
71 * @var bool
72 */
73 protected $isContributionBaseMode = FALSE;
74
74cf4551 75 /**
1728e9a0 76 * This report has been optimised for group filtering.
77 *
78 * CRM-19170
79 *
80 * @var bool
81 */
82 protected $groupFilterNotOptimised = FALSE;
83
84 /**
85 * Class constructor.
74cf4551 86 */
00be9182 87 public function __construct() {
70bea8e2 88 $this->_autoIncludeIndexedFieldsAsOrderBys = 1;
be2fb01f
CW
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],
d7e34fc6 95 'no_field_disambiguation' => TRUE,
be2fb01f
CW
96 ]), [
97 'civicrm_email' => [
a1a2a83d 98 'dao' => 'CRM_Core_DAO_Email',
be2fb01f
CW
99 'fields' => [
100 'email' => [
a1a2a83d
TO
101 'title' => ts('Donor Email'),
102 'default' => TRUE,
be2fb01f
CW
103 ],
104 ],
a1a2a83d 105 'grouping' => 'contact-fields',
be2fb01f
CW
106 ],
107 'civicrm_line_item' => [
4a39d437 108 'dao' => 'CRM_Price_DAO_LineItem',
be2fb01f
CW
109 ],
110 'civicrm_phone' => [
a1a2a83d 111 'dao' => 'CRM_Core_DAO_Phone',
be2fb01f
CW
112 'fields' => [
113 'phone' => [
a1a2a83d
TO
114 'title' => ts('Donor Phone'),
115 'default' => TRUE,
116 'no_repeat' => TRUE,
be2fb01f
CW
117 ],
118 ],
a1a2a83d 119 'grouping' => 'contact-fields',
be2fb01f
CW
120 ],
121 'civicrm_contribution' => [
a1a2a83d 122 'dao' => 'CRM_Contribute_DAO_Contribution',
be2fb01f
CW
123 'fields' => [
124 'contribution_id' => [
a1a2a83d
TO
125 'name' => 'id',
126 'no_display' => TRUE,
127 'required' => TRUE,
be2fb01f
CW
128 ],
129 'list_contri_id' => [
a1a2a83d
TO
130 'name' => 'id',
131 'title' => ts('Contribution ID'),
be2fb01f
CW
132 ],
133 'financial_type_id' => [
a1a2a83d
TO
134 'title' => ts('Financial Type'),
135 'default' => TRUE,
be2fb01f
CW
136 ],
137 'contribution_status_id' => [
a1a2a83d 138 'title' => ts('Contribution Status'),
be2fb01f
CW
139 ],
140 'contribution_page_id' => [
a1a2a83d 141 'title' => ts('Contribution Page'),
be2fb01f
CW
142 ],
143 'source' => [
a1a2a83d 144 'title' => ts('Source'),
be2fb01f
CW
145 ],
146 'payment_instrument_id' => [
a1a2a83d 147 'title' => ts('Payment Type'),
be2fb01f
CW
148 ],
149 'check_number' => [
a1a2a83d 150 'title' => ts('Check Number'),
be2fb01f
CW
151 ],
152 'currency' => [
a1a2a83d
TO
153 'required' => TRUE,
154 'no_display' => TRUE,
be2fb01f 155 ],
a1a2a83d 156 'trxn_id' => NULL,
be2fb01f 157 'receive_date' => ['default' => TRUE],
a1a2a83d 158 'receipt_date' => NULL,
f53a072c 159 'thankyou_date' => NULL,
be2fb01f 160 'total_amount' => [
a1a2a83d
TO
161 'title' => ts('Amount'),
162 'required' => TRUE,
be2fb01f
CW
163 ],
164 'non_deductible_amount' => [
5afce5ad 165 'title' => ts('Non-deductible Amount'),
be2fb01f 166 ],
a1a2a83d
TO
167 'fee_amount' => NULL,
168 'net_amount' => NULL,
be2fb01f 169 'contribution_or_soft' => [
a1a2a83d
TO
170 'title' => ts('Contribution OR Soft Credit?'),
171 'dbAlias' => "'Contribution'",
be2fb01f
CW
172 ],
173 'soft_credits' => [
a1a2a83d
TO
174 'title' => ts('Soft Credits'),
175 'dbAlias' => "NULL",
be2fb01f
CW
176 ],
177 'soft_credit_for' => [
a1a2a83d
TO
178 'title' => ts('Soft Credit For'),
179 'dbAlias' => "NULL",
be2fb01f
CW
180 ],
181 'cancel_date' => [
441a5791 182 'title' => ts('Cancelled / Refunded Date'),
6e7cc0f5 183 'name' => 'contribution_cancel_date',
be2fb01f
CW
184 ],
185 'cancel_reason' => [
441a5791 186 'title' => ts('Cancellation / Refund Reason'),
be2fb01f
CW
187 ],
188 ],
189 'filters' => [
190 'contribution_or_soft' => [
a1a2a83d
TO
191 'title' => ts('Contribution OR Soft Credit?'),
192 'clause' => "(1)",
193 'operatorType' => CRM_Report_Form::OP_SELECT,
194 'type' => CRM_Utils_Type::T_STRING,
be2fb01f 195 'options' => [
a1a2a83d
TO
196 'contributions_only' => ts('Contributions Only'),
197 'soft_credits_only' => ts('Soft Credits Only'),
f072e08f 198 'both' => ts('Both'),
be2fb01f
CW
199 ],
200 ],
201 'receive_date' => ['operatorType' => CRM_Report_Form::OP_DATE],
202 'thankyou_date' => ['operatorType' => CRM_Report_Form::OP_DATE],
203 'contribution_source' => [
db96d968 204 'title' => ts('Source'),
205 'name' => 'source',
206 'type' => CRM_Utils_Type::T_STRING,
be2fb01f
CW
207 ],
208 'currency' => [
fd6a6828 209 'title' => ts('Currency'),
a1a2a83d
TO
210 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
211 'options' => CRM_Core_OptionGroup::values('currencies_enabled'),
212 'default' => NULL,
213 'type' => CRM_Utils_Type::T_STRING,
be2fb01f
CW
214 ],
215 'non_deductible_amount' => [
5afce5ad 216 'title' => ts('Non-deductible Amount'),
be2fb01f
CW
217 ],
218 'financial_type_id' => [
a1a2a83d
TO
219 'title' => ts('Financial Type'),
220 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
4f166b15 221 'options' => CRM_Financial_BAO_FinancialType::getAvailableFinancialTypes(),
a1a2a83d 222 'type' => CRM_Utils_Type::T_INT,
be2fb01f
CW
223 ],
224 'contribution_page_id' => [
a1a2a83d
TO
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,
be2fb01f
CW
229 ],
230 'payment_instrument_id' => [
a1a2a83d
TO
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,
be2fb01f
CW
235 ],
236 'contribution_status_id' => [
a1a2a83d
TO
237 'title' => ts('Contribution Status'),
238 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
239 'options' => CRM_Contribute_PseudoConstant::contributionStatus(),
be2fb01f 240 'default' => [1],
a1a2a83d 241 'type' => CRM_Utils_Type::T_INT,
be2fb01f
CW
242 ],
243 'total_amount' => ['title' => ts('Contribution Amount')],
244 'cancel_date' => [
441a5791 245 'title' => ts('Cancelled / Refunded Date'),
246 'operatorType' => CRM_Report_Form::OP_DATE,
6e7cc0f5 247 'name' => 'contribution_cancel_date',
be2fb01f
CW
248 ],
249 'cancel_reason' => [
441a5791 250 'title' => ts('Cancellation / Refund Reason'),
be2fb01f
CW
251 ],
252 ],
253 'order_bys' => [
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')],
259 ],
260 'group_bys' => [
261 'contribution_id' => [
81a22d3d 262 'name' => 'id',
263 'required' => TRUE,
d70ada18 264 'default' => TRUE,
81a22d3d 265 'title' => ts('Contribution'),
be2fb01f
CW
266 ],
267 ],
a1a2a83d 268 'grouping' => 'contri-fields',
be2fb01f
CW
269 ],
270 'civicrm_contribution_soft' => [
a1a2a83d 271 'dao' => 'CRM_Contribute_DAO_ContributionSoft',
be2fb01f
CW
272 'fields' => [
273 'soft_credit_type_id' => ['title' => ts('Soft Credit Type')],
d70ada18 274 'soft_credit_amount' => ['title' => ts('Soft Credit amount'), 'name' => 'amount', 'type' => CRM_Utils_Type::T_MONEY],
be2fb01f
CW
275 ],
276 'filters' => [
277 'soft_credit_type_id' => [
fd6a6828 278 'title' => ts('Soft Credit Type'),
a1a2a83d
TO
279 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
280 'options' => CRM_Core_OptionGroup::values('soft_credit_type'),
281 'default' => NULL,
282 'type' => CRM_Utils_Type::T_STRING,
be2fb01f
CW
283 ],
284 ],
285 'group_bys' => [
286 'soft_credit_id' => [
d70ada18 287 'name' => 'id',
288 'title' => ts('Soft Credit'),
be2fb01f
CW
289 ],
290 ],
291 ],
292 'civicrm_financial_trxn' => [
e5f5c94b 293 'dao' => 'CRM_Financial_DAO_FinancialTrxn',
be2fb01f
CW
294 'fields' => [
295 'card_type_id' => [
d72b084a 296 'title' => ts('Credit Card Type'),
be2fb01f
CW
297 ],
298 ],
299 'filters' => [
300 'card_type_id' => [
d72b084a 301 'title' => ts('Credit Card Type'),
e5f5c94b 302 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
5e0343e8 303 'options' => CRM_Financial_DAO_FinancialTrxn::buildOptions('card_type_id'),
e5f5c94b
E
304 'default' => NULL,
305 'type' => CRM_Utils_Type::T_STRING,
be2fb01f
CW
306 ],
307 ],
308 ],
309 'civicrm_batch' => [
433465bc
PN
310 'dao' => 'CRM_Batch_DAO_EntityBatch',
311 'grouping' => 'contri-fields',
be2fb01f
CW
312 'fields' => [
313 'batch_id' => [
49d516c6 314 'name' => 'batch_id',
433465bc 315 'title' => ts('Batch Name'),
be2fb01f
CW
316 ],
317 ],
318 'filters' => [
319 'bid' => [
433465bc
PN
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',
be2fb01f
CW
325 ],
326 ],
327 ],
328 'civicrm_contribution_ordinality' => [
a1a2a83d
TO
329 'dao' => 'CRM_Contribute_DAO_Contribution',
330 'alias' => 'cordinality',
be2fb01f
CW
331 'filters' => [
332 'ordinality' => [
a1a2a83d
TO
333 'title' => ts('Contribution Ordinality'),
334 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
be2fb01f 335 'options' => [
a1a2a83d
TO
336 0 => 'First by Contributor',
337 1 => 'Second or Later by Contributor',
be2fb01f 338 ],
a1a2a83d 339 'type' => CRM_Utils_Type::T_INT,
be2fb01f
CW
340 ],
341 ],
342 ],
343 'civicrm_note' => [
a1a2a83d 344 'dao' => 'CRM_Core_DAO_Note',
be2fb01f
CW
345 'fields' => [
346 'contribution_note' => [
a1a2a83d
TO
347 'name' => 'note',
348 'title' => ts('Contribution Note'),
be2fb01f
CW
349 ],
350 ],
351 'filters' => [
352 'note' => [
a1a2a83d
TO
353 'name' => 'note',
354 'title' => ts('Contribution Note'),
355 'operator' => 'like',
356 'type' => CRM_Utils_Type::T_STRING,
be2fb01f
CW
357 ],
358 ],
359 ],
360 ]) + $this->addAddressFields(FALSE);
a703d90c 361 // The tests test for this variation of the sort_name field. Don't argue with the tests :-).
25cf1ac5 362 $this->_columns['civicrm_contact']['fields']['sort_name']['title'] = ts('Donor Name');
16e2e80c 363 $this->_groupFilter = TRUE;
6a488035 364 $this->_tagFilter = TRUE;
9db913d3 365 // If we have campaigns enabled, add those elements to both the fields, filters and sorting
366 $this->addCampaignFields('civicrm_contribution', FALSE, TRUE);
7a961f19 367
368 $this->_currencyColumn = 'civicrm_contribution_currency';
6a488035
TO
369 parent::__construct();
370 }
371
477be9cc
AH
372 /**
373 * Validate incompatible report settings.
374 *
375 * @return bool
376 * true if no error found
377 */
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');
de6c59ca 383 if (!empty($groupBySoft['soft_credit_id'])) {
477be9cc
AH
384 $this->setElementError('group_bys', ts('You cannot group by soft credit when displaying contributions only. Please uncheck "Soft Credit" in the Grouping tab.'));
385 }
386 }
387
388 return parent::validate();
389 }
390
74cf4551 391 /**
8b7f2513 392 * Set the FROM clause for the report.
74cf4551 393 */
8b7f2513 394 public function from() {
97b4dc6b 395 $this->setFromBase('civicrm_contact');
396 $this->_from .= "
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";
40c655aa 400
d70ada18 401 $this->joinContributionToSoftCredit();
8b7f2513 402 $this->appendAdditionalFromJoins();
6a488035
TO
403 }
404
74cf4551
EM
405 /**
406 * @param $rows
407 *
408 * @return array
409 */
00be9182 410 public function statistics(&$rows) {
6a488035
TO
411 $statistics = parent::statistics($rows);
412
be2fb01f 413 $totalAmount = $average = $fees = $net = [];
7a961f19 414 $count = 0;
6a488035
TO
415 $select = "
416 SELECT COUNT({$this->_aliases['civicrm_contribution']}.total_amount ) as count,
417 SUM( {$this->_aliases['civicrm_contribution']}.total_amount ) as amount,
7a961f19 418 ROUND(AVG({$this->_aliases['civicrm_contribution']}.total_amount), 2) as avg,
7010bab9 419 {$this->_aliases['civicrm_contribution']}.currency as currency,
e468f7a2 420 SUM( {$this->_aliases['civicrm_contribution']}.fee_amount ) as fees,
7010bab9 421 SUM( {$this->_aliases['civicrm_contribution']}.net_amount ) as net
6a488035
TO
422 ";
423
7a961f19 424 $group = "\nGROUP BY {$this->_aliases['civicrm_contribution']}.currency";
425 $sql = "{$select} {$this->_from} {$this->_where} {$group}";
6a488035 426 $dao = CRM_Core_DAO::executeQuery($sql);
3b2bbbfb 427 $this->addToDeveloperTab($sql);
6a488035 428
7a961f19 429 while ($dao->fetch()) {
7010bab9
DG
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);
9d72cede 433 $average[] = CRM_Utils_Money::format($dao->avg, $dao->currency);
1c4d8c3e 434 $count += $dao->count;
6a488035 435 }
be2fb01f 436 $statistics['counts']['amount'] = [
be205937 437 'title' => ts('Total Amount (Contributions)'),
7a961f19 438 'value' => implode(', ', $totalAmount),
439 'type' => CRM_Utils_Type::T_STRING,
be2fb01f
CW
440 ];
441 $statistics['counts']['count'] = [
be205937 442 'title' => ts('Total Contributions'),
7a961f19 443 'value' => $count,
be2fb01f
CW
444 ];
445 $statistics['counts']['fees'] = [
7010bab9
DG
446 'title' => ts('Fees'),
447 'value' => implode(', ', $fees),
448 'type' => CRM_Utils_Type::T_STRING,
be2fb01f
CW
449 ];
450 $statistics['counts']['net'] = [
7010bab9
DG
451 'title' => ts('Net'),
452 'value' => implode(', ', $net),
453 'type' => CRM_Utils_Type::T_STRING,
be2fb01f
CW
454 ];
455 $statistics['counts']['avg'] = [
7a961f19 456 'title' => ts('Average'),
457 'value' => implode(', ', $average),
458 'type' => CRM_Utils_Type::T_STRING,
be2fb01f 459 ];
6a488035 460
70c41bce 461 // Stats for soft credits
9d72cede
EM
462 if ($this->_softFrom &&
463 CRM_Utils_Array::value('contribution_or_soft_value', $this->_params) !=
464 'contributions_only'
465 ) {
be2fb01f 466 $totalAmount = $average = [];
9d72cede 467 $count = 0;
c548e07b 468 $select = "
70c41bce
DS
469SELECT 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";
c548e07b 473 $sql = "
f813f78e 474{$select}
475{$this->_softFrom}
c548e07b
DS
476GROUP BY {$this->_aliases['civicrm_contribution']}.currency";
477 $dao = CRM_Core_DAO::executeQuery($sql);
3b2bbbfb 478 $this->addToDeveloperTab($sql);
c548e07b 479 while ($dao->fetch()) {
6c552737 480 $totalAmount[] = CRM_Utils_Money::format($dao->amount, $dao->currency) . " (" .
9d72cede
EM
481 $dao->count . ")";
482 $average[] = CRM_Utils_Money::format($dao->avg, $dao->currency);
c548e07b
DS
483 $count += $dao->count;
484 }
be2fb01f 485 $statistics['counts']['softamount'] = [
c548e07b
DS
486 'title' => ts('Total Amount (Soft Credits)'),
487 'value' => implode(', ', $totalAmount),
488 'type' => CRM_Utils_Type::T_STRING,
be2fb01f
CW
489 ];
490 $statistics['counts']['softcount'] = [
c548e07b
DS
491 'title' => ts('Total Soft Credits'),
492 'value' => $count,
be2fb01f
CW
493 ];
494 $statistics['counts']['softavg'] = [
c548e07b
DS
495 'title' => ts('Average (Soft Credits)'),
496 'value' => implode(', ', $average),
497 'type' => CRM_Utils_Type::T_STRING,
be2fb01f 498 ];
70c41bce 499 }
70c41bce 500
6a488035
TO
501 return $statistics;
502 }
503
8b7f2513 504 /**
e6bab5ea 505 * Build the report query.
8b7f2513 506 *
e6bab5ea 507 * @param bool $applyLimit
8b7f2513 508 *
e6bab5ea 509 * @return string
8b7f2513 510 */
4a3c55d9 511 public function buildQuery($applyLimit = FALSE) {
e6bab5ea 512 if ($this->isTempTableBuilt) {
4a3c55d9 513 $this->limit();
fe42be71 514 return "SELECT SQL_CALC_FOUND_ROWS * FROM {$this->temporaryTables['civireport_contribution_detail_temp3']['name']} $this->_orderBy $this->_limit";
e6bab5ea 515 }
516 return parent::buildQuery($applyLimit);
517 }
70c41bce 518
e6bab5ea 519 /**
520 * Shared function for preliminary processing.
521 *
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'.
524 */
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;
531 }
51fa20cb 532
a3b2dd00 533 if (CRM_Utils_Array::value('contribution_or_soft_value', $this->_params, 'contributions_only') == 'contributions_only') {
e6bab5ea 534 $this->isContributionBaseMode = TRUE;
535 }
536 if ($this->isContributionBaseMode &&
537 (!empty($this->_params['fields']['soft_credit_type_id'])
538 || !empty($this->_params['soft_credit_type_id_value']))
539 ) {
540 unset($this->_params['fields']['soft_credit_type_id']);
541 if (!empty($this->_params['soft_credit_type_id_value'])) {
be2fb01f 542 $this->_params['soft_credit_type_id_value'] = [];
e6bab5ea 543 CRM_Core_Session::setStatus(ts('Is it not possible to filter on soft contribution type when not including soft credits.'));
544 }
545 }
70c41bce
DS
546 // 1. use main contribution query to build temp table 1
547 $sql = $this->buildQuery();
d70ada18 548 $this->createTemporaryTable('civireport_contribution_detail_temp1', $sql);
70c41bce 549
c548e07b 550 // 2. customize main contribution query for soft credit, and build temp table 2 with soft credit contributions only
e6bab5ea 551 $this->queryMode = 'SoftCredit';
552 // Rebuild select with no groupby. Do not let column headers change.
553 $headers = $this->_columnHeaders;
554 $this->select();
555 $this->_columnHeaders = $headers;
8b7f2513 556 $this->softCreditFrom();
be78fc2d
DL
557 // also include custom group from if included
558 // since this might be included in select
559 $this->customDataFrom();
560
70c41bce
DS
561 $select = str_ireplace('contribution_civireport.total_amount', 'contribution_soft_civireport.amount', $this->_select);
562 $select = str_ireplace("'Contribution' as", "'Soft Credit' as", $select);
d70ada18 563
e6bab5ea 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.
d70ada18 566 $sql = "{$select} {$this->_from} {$this->_where} $this->_groupBy";
fe42be71 567 $this->createTemporaryTable('civireport_contribution_detail_temp2', $sql);
e6bab5ea 568
9d72cede
EM
569 if (CRM_Utils_Array::value('contribution_or_soft_value', $this->_params) ==
570 'soft_credits_only'
571 ) {
c548e07b
DS
572 // revise pager : prev, next based on soft-credits only
573 $this->setPager();
f813f78e 574 }
70c41bce
DS
575
576 // copy _from for later use of stats calculation for soft credits, and reset $this->_from to main query
577 $this->_softFrom = $this->_from;
c93f6d83
DL
578
579 // simple reset of ->_from
580 $this->from();
be78fc2d
DL
581
582 // also include custom group from if included
583 // since this might be included in select
c93f6d83 584 $this->customDataFrom();
70c41bce 585
c548e07b 586 // 3. Decide where to populate temp3 table from
e6bab5ea 587 if ($this->isContributionBaseMode
9d72cede 588 ) {
fe42be71 589 $this->createTemporaryTable('civireport_contribution_detail_temp3',
590 "(SELECT * FROM {$this->temporaryTables['civireport_contribution_detail_temp1']['name']})"
e6bab5ea 591 );
9d72cede 592 }
4c9b6178 593 elseif (CRM_Utils_Array::value('contribution_or_soft_value', $this->_params) ==
9d72cede
EM
594 'soft_credits_only'
595 ) {
fe42be71 596 $this->createTemporaryTable('civireport_contribution_detail_temp3',
597 "(SELECT * FROM {$this->temporaryTables['civireport_contribution_detail_temp2']['name']})"
e6bab5ea 598 );
9d72cede
EM
599 }
600 else {
fe42be71 601 $this->createTemporaryTable('civireport_contribution_detail_temp3', "
d70ada18 602(SELECT * FROM {$this->temporaryTables['civireport_contribution_detail_temp1']['name']})
c548e07b 603UNION ALL
fe42be71 604(SELECT * FROM {$this->temporaryTables['civireport_contribution_detail_temp2']['name']})");
c548e07b 605 }
e6bab5ea 606 $this->isTempTableBuilt = TRUE;
6a488035
TO
607 }
608
a9cf37f5 609 /**
e6bab5ea 610 * Store group bys into array - so we can check elsewhere what is grouped.
a9cf37f5 611 *
d70ada18 612 * If we are generating a table of soft credits we need to group by them.
a9cf37f5 613 */
e6bab5ea 614 protected function storeGroupByArray() {
615 if ($this->queryMode === 'SoftCredit') {
d70ada18 616 $this->_groupByArray = [$this->_aliases['civicrm_contribution_soft'] . '.id'];
a9cf37f5 617 }
e6bab5ea 618 else {
619 parent::storeGroupByArray();
a9cf37f5 620 }
621 }
622
74cf4551 623 /**
ced9bfed
EM
624 * Alter display of rows.
625 *
626 * Iterate through the rows retrieved via SQL and make changes for display purposes,
627 * such as rendering contacts as links.
628 *
629 * @param array $rows
630 * Rows generated by SQL, with an array for each row.
74cf4551 631 */
00be9182 632 public function alterDisplay(&$rows) {
9d72cede
EM
633 $entryFound = FALSE;
634 $display_flag = $prev_cid = $cid = 0;
635 $contributionTypes = CRM_Contribute_PseudoConstant::financialType();
6a488035
TO
636 $contributionStatus = CRM_Contribute_PseudoConstant::contributionStatus();
637 $paymentInstruments = CRM_Contribute_PseudoConstant::paymentInstrument();
9d72cede 638 $contributionPages = CRM_Contribute_PseudoConstant::contributionPage();
433465bc 639 $batches = CRM_Batch_BAO_Batch::getBatches();
6a488035
TO
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']) {
645 if ($rowNum == 0) {
646 $prev_cid = $cid;
647 }
648 else {
649 if ($prev_cid == $cid) {
650 $display_flag = 1;
651 $prev_cid = $cid;
652 }
653 else {
654 $display_flag = 0;
655 $prev_cid = $cid;
656 }
657 }
658
659 if ($display_flag) {
660 foreach ($row as $colName => $colVal) {
661 // Hide repeats in no-repeat columns, but not if the field's a section header
9d72cede
EM
662 if (in_array($colName, $this->_noRepeats) &&
663 !array_key_exists($colName, $this->_sections)
664 ) {
6a488035
TO
665 unset($rows[$rowNum][$colName]);
666 }
667 }
668 }
669 $entryFound = TRUE;
670 }
671 }
672 }
673
9d72cede
EM
674 if (CRM_Utils_Array::value('civicrm_contribution_contribution_or_soft', $rows[$rowNum]) ==
675 'Contribution'
676 ) {
8e3ad5e0
WA
677 unset($rows[$rowNum]['civicrm_contribution_soft_soft_credit_type_id']);
678 }
6a488035 679
a703d90c 680 $entryFound = $this->alterDisplayContactFields($row, $rows, $rowNum, 'contribution/detail', ts('View Contribution Details')) ? TRUE : $entryFound;
6a488035 681 // convert donor sort name to link
9d72cede
EM
682 if (array_key_exists('civicrm_contact_sort_name', $row) &&
683 !empty($rows[$rowNum]['civicrm_contact_sort_name']) &&
6a488035
TO
684 array_key_exists('civicrm_contact_id', $row)
685 ) {
686 $url = CRM_Utils_System::url("civicrm/contact/view",
687 'reset=1&cid=' . $row['civicrm_contact_id'],
688 $this->_absoluteUrl
689 );
690 $rows[$rowNum]['civicrm_contact_sort_name_link'] = $url;
691 $rows[$rowNum]['civicrm_contact_sort_name_hover'] = ts("View Contact Summary for this Contact.");
692 }
693
6a488035
TO
694 if ($value = CRM_Utils_Array::value('civicrm_contribution_financial_type_id', $row)) {
695 $rows[$rowNum]['civicrm_contribution_financial_type_id'] = $contributionTypes[$value];
696 $entryFound = TRUE;
697 }
698 if ($value = CRM_Utils_Array::value('civicrm_contribution_contribution_status_id', $row)) {
699 $rows[$rowNum]['civicrm_contribution_contribution_status_id'] = $contributionStatus[$value];
700 $entryFound = TRUE;
701 }
0d0e945a
DG
702 if ($value = CRM_Utils_Array::value('civicrm_contribution_contribution_page_id', $row)) {
703 $rows[$rowNum]['civicrm_contribution_contribution_page_id'] = $contributionPages[$value];
704 $entryFound = TRUE;
705 }
6a488035
TO
706 if ($value = CRM_Utils_Array::value('civicrm_contribution_payment_instrument_id', $row)) {
707 $rows[$rowNum]['civicrm_contribution_payment_instrument_id'] = $paymentInstruments[$value];
708 $entryFound = TRUE;
709 }
433465bc
PN
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);
6a488035
TO
712 $entryFound = TRUE;
713 }
5e0343e8 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');
e5f5c94b
E
716 $entryFound = TRUE;
717 }
6a488035
TO
718
719 // Contribution amount links to viewing contribution
cdc61ccc
AH
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",
725 [
726 'reset' => 1,
727 'id' => $row['civicrm_contribution_contribution_id'],
728 'cid' => $row['civicrm_contact_id'],
729 'action' => 'view',
730 'context' => 'contribution',
731 'selectedChild' => 'contribute',
732 ],
733 $this->_absoluteUrl
734 );
735 $rows[$rowNum]['civicrm_contribution_total_amount_link'] = $url;
736 $rows[$rowNum]['civicrm_contribution_total_amount_hover'] = ts("View Details of this Contribution.");
737 }
6a488035
TO
738 $entryFound = TRUE;
739 }
740
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']) {
9db913d3 744 $rows[$rowNum]['civicrm_contribution_campaign_id'] = $this->campaigns[$value];
6a488035
TO
745 $entryFound = TRUE;
746 }
747 }
748
70c41bce
DS
749 // soft credits
750 if (array_key_exists('civicrm_contribution_soft_credits', $row) &&
9d72cede
EM
751 'Contribution' ==
752 CRM_Utils_Array::value('civicrm_contribution_contribution_or_soft', $rows[$rowNum]) &&
70c41bce
DS
753 array_key_exists('civicrm_contribution_contribution_id', $row)
754 ) {
755 $query = "
67aad585 756SELECT civicrm_contact_id, civicrm_contact_sort_name, civicrm_contribution_total_amount, civicrm_contribution_currency
fe42be71 757FROM {$this->temporaryTables['civireport_contribution_detail_temp2']['name']}
70c41bce 758WHERE civicrm_contribution_contribution_id={$row['civicrm_contribution_contribution_id']}";
9d72cede 759 $dao = CRM_Core_DAO::executeQuery($query);
70c41bce 760 $string = '';
1ebef749 761 $separator = ($this->_outputMode !== 'csv') ? "<br/>" : ' ';
70c41bce 762 while ($dao->fetch()) {
9d72cede
EM
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> " .
67aad585 767 CRM_Utils_Money::format($dao->civicrm_contribution_total_amount, $dao->civicrm_contribution_currency);
70c41bce
DS
768 }
769 $rows[$rowNum]['civicrm_contribution_soft_credits'] = $string;
770 }
771
772 if (array_key_exists('civicrm_contribution_soft_credit_for', $row) &&
9d72cede
EM
773 'Soft Credit' ==
774 CRM_Utils_Array::value('civicrm_contribution_contribution_or_soft', $rows[$rowNum]) &&
70c41bce
DS
775 array_key_exists('civicrm_contribution_contribution_id', $row)
776 ) {
777 $query = "
f813f78e 778SELECT civicrm_contact_id, civicrm_contact_sort_name
d70ada18 779FROM {$this->temporaryTables['civireport_contribution_detail_temp1']['name']}
70c41bce 780WHERE civicrm_contribution_contribution_id={$row['civicrm_contribution_contribution_id']}";
9d72cede 781 $dao = CRM_Core_DAO::executeQuery($query);
70c41bce
DS
782 $string = '';
783 while ($dao->fetch()) {
9d72cede
EM
784 $url = CRM_Utils_System::url("civicrm/contact/view", 'reset=1&cid=' .
785 $dao->civicrm_contact_id);
786 $string = $string .
787 "\n<a href='{$url}'>{$dao->civicrm_contact_sort_name}</a>";
70c41bce
DS
788 }
789 $rows[$rowNum]['civicrm_contribution_soft_credit_for'] = $string;
790 }
791
e468f7a2 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']);
796 }
797
51fa20cb 798 //convert soft_credit_type_id into label
799 if (array_key_exists('civicrm_contribution_soft_soft_credit_type_id', $rows[$rowNum])) {
7a4192f7 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']
804 );
51fa20cb 805 }
806
6a488035
TO
807 $entryFound = $this->alterDisplayAddressFields($row, $rows, $rowNum, 'contribute/detail', 'List all contribution(s) for this ') ? TRUE : $entryFound;
808
809 // skip looking further in rows, if first row itself doesn't
810 // have the column we need
811 if (!$entryFound) {
812 break;
813 }
814 $lastKey = $rowNum;
815 }
816 }
1c4d8c3e 817
00be9182 818 public function sectionTotals() {
6a488035
TO
819
820 // Reports using order_bys with sections must populate $this->_selectAliases in select() method.
821 if (empty($this->_selectAliases)) {
822 return;
823 }
824
825 if (!empty($this->_sections)) {
826 // build the query with no LIMIT clause
9d72cede 827 $select = str_ireplace('SELECT SQL_CALC_FOUND_ROWS ', 'SELECT ', $this->_select);
6a488035
TO
828 $sql = "{$select} {$this->_from} {$this->_where} {$this->_groupBy} {$this->_having} {$this->_orderBy}";
829
830 // pull section aliases out of $this->_sections
831 $sectionAliases = array_keys($this->_sections);
832
be2fb01f 833 $ifnulls = [];
6a488035
TO
834 foreach (array_merge($sectionAliases, $this->_selectAliases) as $alias) {
835 $ifnulls[] = "ifnull($alias, '') as $alias";
836 }
b708c08d 837 $this->_select = "SELECT " . implode(", ", $ifnulls);
36d2f4d5 838 $this->_select = CRM_Contact_BAO_Query::appendAnyValueToSelect($ifnulls, $sectionAliases);
6a488035
TO
839
840 /* Group (un-limited) report by all aliases and get counts. This might
e70a7fc0
TO
841 * be done more efficiently when the contents of $sql are known, ie. by
842 * overriding this method in the report class.
843 */
6a488035
TO
844
845 $addtotals = '';
846
67aad585 847 if (array_search("civicrm_contribution_total_amount", $this->_selectAliases) !==
9d72cede
EM
848 FALSE
849 ) {
67aad585 850 $addtotals = ", sum(civicrm_contribution_total_amount) as sumcontribs";
6a488035
TO
851 $showsumcontribs = TRUE;
852 }
853
b708c08d 854 $query = $this->_select .
fe42be71 855 "$addtotals, count(*) as ct from {$this->temporaryTables['civireport_contribution_detail_temp3']['name']} group by " .
9d72cede 856 implode(", ", $sectionAliases);
6a488035 857 // initialize array of total counts
be2fb01f 858 $sumcontribs = $totals = [];
6a488035 859 $dao = CRM_Core_DAO::executeQuery($query);
3b2bbbfb 860 $this->addToDeveloperTab($query);
6a488035
TO
861 while ($dao->fetch()) {
862
863 // let $this->_alterDisplay translate any integer ids to human-readable values.
864 $rows[0] = $dao->toArray();
865 $this->alterDisplay($rows);
866 $row = $rows[0];
867
868 // add totals for all permutations of section values
be2fb01f 869 $values = [];
6a488035
TO
870 $i = 1;
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;
9d72cede
EM
878 if ($showsumcontribs) {
879 $sumcontribs[$key] = $dao->sumcontribs;
880 }
1c4d8c3e 881 }
6a488035
TO
882 else {
883 // other aliases are higher level; roll count into their total
84178120 884 $totals[$key] = (array_key_exists($key, $totals)) ? $totals[$key] + $dao->ct : $dao->ct;
1c4d8c3e 885 if ($showsumcontribs) {
84178120 886 $sumcontribs[$key] = array_key_exists($key, $sumcontribs) ? $sumcontribs[$key] + $dao->sumcontribs : $dao->sumcontribs;
6a488035
TO
887 }
888 }
889 }
890 }
891 if ($showsumcontribs) {
be2fb01f 892 $totalandsum = [];
0161a899
ML
893 // ts exception to avoid having ts("%1 %2: %3")
894 $title = '%1 contributions / soft-credits: %2';
895
9d72cede
EM
896 if (CRM_Utils_Array::value('contribution_or_soft_value', $this->_params) ==
897 'contributions_only'
898 ) {
0161a899 899 $title = '%1 contributions: %2';
9d72cede 900 }
4c9b6178 901 elseif (CRM_Utils_Array::value('contribution_or_soft_value', $this->_params) ==
9d72cede
EM
902 'soft_credits_only'
903 ) {
0161a899 904 $title = '%1 soft-credits: %2';
bec9ef32 905 }
6a488035 906 foreach ($totals as $key => $total) {
be2fb01f 907 $totalandsum[$key] = ts($title, [
1c4d8c3e 908 1 => $total,
21dfd5f5 909 2 => CRM_Utils_Money::format($sumcontribs[$key]),
be2fb01f 910 ]);
6a488035
TO
911 }
912 $this->assign('sectionTotals', $totalandsum);
913 }
914 else {
915 $this->assign('sectionTotals', $totals);
916 }
917 }
918 }
96025800 919
8b7f2513 920 /**
921 * Generate the from clause as it relates to the soft credits.
922 */
923 public function softCreditFrom() {
924
97b4dc6b 925 $this->_from = "
d70ada18 926 FROM {$this->temporaryTables['civireport_contribution_detail_temp1']['name']} temp1_civireport
97b4dc6b 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
933 {$this->_aclFrom}
934 ";
8b7f2513 935
5e3dec81
JP
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']);
939 }
8b7f2513 940 $this->appendAdditionalFromJoins();
941 }
942
943 /**
944 * Append the joins that are required regardless of context.
945 */
946 public function appendAdditionalFromJoins() {
947 if (!empty($this->_params['ordinality_value'])) {
948 $this->_from .= "
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";
951 }
3b2bbbfb 952 $this->joinPhoneFromContact();
953 $this->joinAddressFromContact();
954 $this->joinEmailFromContact();
8b7f2513 955
8b7f2513 956 // include contribution note
957 if (!empty($this->_params['fields']['contribution_note']) ||
958 !empty($this->_params['note_value'])
959 ) {
960 $this->_from .= "
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 )";
964 }
965 //for contribution batches
966 if (!empty($this->_params['fields']['batch_id']) ||
967 !empty($this->_params['bid_value'])
968 ) {
969 $this->_from .= "
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')";
976 }
e5f5c94b 977 // for credit card type
125bbb86 978 $this->addFinancialTrxnFromClause();
8b7f2513 979 }
980
d70ada18 981 /**
982 * Add join to the soft credit table.
983 */
984 protected function joinContributionToSoftCredit() {
76faaa00
AH
985 if (CRM_Utils_Array::value('contribution_or_soft_value', $this->_params) == 'contributions_only'
986 && !$this->isTableSelected('civicrm_contribution_soft')) {
d70ada18 987 return;
988 }
989 $joinType = ' LEFT ';
990 if (CRM_Utils_Array::value('contribution_or_soft_value', $this->_params) == 'soft_credits_only') {
991 $joinType = ' INNER ';
992 }
993 $this->_from .= "
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
996 ";
997 }
998
6a488035 999}