INFRA-132 - Move stray comments into docblocks
[civicrm-core.git] / CRM / Report / Form / Contribute / Repeat.php
CommitLineData
6a488035 1<?php
6a488035
TO
2
3/*
4 +--------------------------------------------------------------------+
39de6fd5 5 | CiviCRM version 4.6 |
6a488035 6 +--------------------------------------------------------------------+
06b69b18 7 | Copyright CiviCRM LLC (c) 2004-2014 |
6a488035
TO
8 +--------------------------------------------------------------------+
9 | This file is a part of CiviCRM. |
10 | |
11 | CiviCRM is free software; you can copy, modify, and distribute it |
12 | under the terms of the GNU Affero General Public License |
13 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
14 | |
15 | CiviCRM is distributed in the hope that it will be useful, but |
16 | WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
18 | See the GNU Affero General Public License for more details. |
19 | |
20 | You should have received a copy of the GNU Affero General Public |
21 | License and the CiviCRM Licensing Exception along |
22 | with this program; if not, contact CiviCRM LLC |
23 | at info[AT]civicrm[DOT]org. If you have questions about the |
24 | GNU Affero General Public License or the licensing of CiviCRM, |
25 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
26 +--------------------------------------------------------------------+
27*/
28
29/**
30 *
31 * @package CRM
06b69b18 32 * @copyright CiviCRM LLC (c) 2004-2014
6a488035
TO
33 * $Id$
34 *
35 */
36class CRM_Report_Form_Contribute_Repeat extends CRM_Report_Form {
37 protected $_amountClauseWithAND = NULL;
38
39 public $_drilldownReport = array('contribute/detail' => 'Link to Detail Report');
40
74cf4551 41 /**
74cf4551
EM
42 */
43 /**
74cf4551 44 */
00be9182 45 public function __construct() {
6a488035 46 $this->_columns = array(
9d72cede 47 'civicrm_contact' => array(
6a488035
TO
48 'dao' => 'CRM_Contact_DAO_Contact',
49 'grouping' => 'contact-fields',
9d72cede
EM
50 'fields' => array(
51 'sort_name' => array(
6a488035
TO
52 'title' => ts('Contact Name'),
53 'no_repeat' => TRUE,
54 'default' => TRUE,
55 ),
9d72cede 56 'display_name' => array(
6a488035
TO
57 'title' => ts('Display Name'),
58 'no_repeat' => TRUE,
59 ),
9d72cede 60 'addressee_display' => array(
6a488035
TO
61 'title' => ts('Addressee Name'),
62 'no_repeat' => TRUE,
63 ),
9d72cede 64 'id' => array(
6a488035
TO
65 'no_display' => TRUE,
66 'required' => TRUE,
67 ),
9d72cede 68 'contact_type' => array(
30f85891
RN
69 'title' => ts('Contact Type'),
70 'no_repeat' => TRUE,
71 ),
9d72cede 72 'contact_sub_type' => array(
b8f96eb8 73 'title' => ts('Contact Subtype'),
30f85891
RN
74 'no_repeat' => TRUE,
75 ),
6a488035 76 ),
9d72cede
EM
77 'filters' => array(
78 'percentage_change' => array(
6a488035
TO
79 'title' => ts('Percentage Change'),
80 'type' => CRM_Utils_Type::T_INT,
81 'operatorType' => CRM_Report_Form::OP_INT,
82 'name' => 'percentage_change',
83 'dbAlias' => '( ( contribution_civireport2.total_amount_sum - contribution_civireport1.total_amount_sum ) * 100 / contribution_civireport1.total_amount_sum )',
84 ),
85 ),
9d72cede
EM
86 'group_bys' => array(
87 'id' => array(
6a488035
TO
88 'title' => ts('Contact'),
89 'default' => TRUE,
90 ),
91 ),
92 ),
9d72cede 93 'civicrm_email' => array(
6a488035 94 'dao' => 'CRM_Core_DAO_Email',
9d72cede
EM
95 'fields' => array(
96 'email' => array(
6a488035
TO
97 'title' => ts('Email'),
98 'no_repeat' => TRUE,
99 ),
100 ),
101 'grouping' => 'contact-fields',
102 ),
9d72cede 103 'civicrm_phone' => array(
6a488035 104 'dao' => 'CRM_Core_DAO_Phone',
9d72cede
EM
105 'fields' => array(
106 'phone' => array(
6a488035
TO
107 'title' => ts('Phone'),
108 'no_repeat' => TRUE,
109 ),
110 ),
111 'grouping' => 'contact-fields',
112 ),
9d72cede 113 'civicrm_address' => array(
6a488035
TO
114 'dao' => 'CRM_Core_DAO_Address',
115 'grouping' => 'contact-fields',
9d72cede 116 'fields' => array(
6a488035 117 'street_address' => array('title' => ts('Street Address')),
c29131ec 118 'supplemental_address_1' => array('title' => ts('Supplemental Address 1')),
6a488035
TO
119 'city' => array('title' => ts('City')),
120 'country_id' => array('title' => ts('Country')),
121 'state_province_id' => array('title' => ts('State/Province')),
122 'postal_code' => array('title' => ts('Postal Code')),
123 ),
9d72cede 124 'group_bys' => array(
6a488035 125 'country_id' => array('title' => ts('Country')),
9d72cede
EM
126 'state_province_id' => array(
127 'title' => ts('State/Province'),
6a488035
TO
128 ),
129 ),
130 ),
9d72cede 131 'civicrm_financial_type' => array(
b914f4e8
PN
132 'dao' => 'CRM_Financial_DAO_FinancialType',
133 'fields' => array('financial_type' => array('title' => ts('Financial Type'))),
9d72cede
EM
134 'grouping' => 'contri-fields',
135 'group_bys' => array(
136 'financial_type' => array(
137 'name' => 'id',
138 'title' => ts('Financial Type'),
139 ),
6a488035
TO
140 ),
141 ),
9d72cede 142 'civicrm_contribution' => array(
6a488035 143 'dao' => 'CRM_Contribute_DAO_Contribution',
9d72cede 144 'fields' => array(
6a488035 145 'contribution_source' => NULL,
9d72cede 146 'total_amount1' => array(
6a488035
TO
147 'name' => 'total_amount',
148 'alias' => 'contribution1',
149 'title' => ts('Range One Stat'),
150 'type' => CRM_Utils_Type::T_MONEY,
151 'default' => TRUE,
152 'required' => TRUE,
153 'clause' => '
2f4c2f5d 154contribution_civireport1.total_amount_count as contribution1_total_amount_count,
6a488035
TO
155contribution_civireport1.total_amount_sum as contribution1_total_amount_sum',
156 ),
9d72cede 157 'total_amount2' => array(
6a488035
TO
158 'name' => 'total_amount',
159 'alias' => 'contribution2',
160 'title' => ts('Range Two Stat'),
161 'type' => CRM_Utils_Type::T_MONEY,
162 'default' => TRUE,
163 'required' => TRUE,
164 'clause' => '
2f4c2f5d 165contribution_civireport2.total_amount_count as contribution2_total_amount_count,
6a488035
TO
166contribution_civireport2.total_amount_sum as contribution2_total_amount_sum',
167 ),
168 ),
169 'grouping' => 'contri-fields',
9d72cede
EM
170 'filters' => array(
171 'receive_date1' => array(
6a488035
TO
172 'title' => ts('Initial Date Range'),
173 'default' => 'previous.year',
174 'type' => CRM_Utils_Type::T_DATE,
175 'operatorType' => CRM_Report_Form::OP_DATE,
176 'name' => 'receive_date',
177 ),
9d72cede 178 'receive_date2' => array(
6a488035
TO
179 'title' => ts('Second Date Range'),
180 'default' => 'this.year',
181 'type' => CRM_Utils_Type::T_DATE,
182 'operatorType' => CRM_Report_Form::OP_DATE,
183 'name' => 'receive_date',
184 ),
9d72cede 185 'total_amount1' => array(
6a488035
TO
186 'title' => ts('Range One Amount'),
187 'type' => CRM_Utils_Type::T_INT,
188 'operatorType' => CRM_Report_Form::OP_INT,
189 'name' => 'total_amount',
190 ),
9d72cede 191 'total_amount2' => array(
6a488035
TO
192 'title' => ts('Range Two Amount'),
193 'type' => CRM_Utils_Type::T_INT,
194 'operatorType' => CRM_Report_Form::OP_INT,
195 'name' => 'total_amount',
196 ),
9d72cede 197 'financial_type_id' => array(
b914f4e8 198 'title' => ts('Financial Type'),
6a488035 199 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
b914f4e8 200 'options' => CRM_Contribute_PseudoConstant::financialType(),
6a488035 201 ),
9d72cede 202 'contribution_status_id' => array(
b914f4e8 203 'title' => ts('Contribution Status'),
6a488035
TO
204 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
205 'options' => CRM_Contribute_PseudoConstant::contributionStatus(),
206 'default' => array('1'),
207 ),
208 ),
209 'group_bys' => array('contribution_source' => NULL),
210 ),
6a488035
TO
211 );
212
16e2e80c 213 $this->_groupFilter = TRUE;
6a488035
TO
214 $this->_tagFilter = TRUE;
215
216 parent::__construct();
217 }
218
00be9182 219 public function preProcess() {
6a488035
TO
220 parent::preProcess();
221 }
222
74cf4551
EM
223 /**
224 * @param bool $freeze
225 *
226 * @return array
227 */
00be9182 228 public function setDefaultValues($freeze = TRUE) {
6a488035
TO
229 return parent::setDefaultValues($freeze);
230 }
231
00be9182 232 public function select() {
6a488035
TO
233 $select = array();
234 $append = NULL;
235 // since contact fields not related to financial type
b914f4e8 236 if (array_key_exists('financial_type', $this->_params['group_bys']) ||
6a488035
TO
237 array_key_exists('contribution_source', $this->_params['group_bys'])
238 ) {
239 unset($this->_columns['civicrm_contact']['fields']['id']);
240 }
241
242 foreach ($this->_columns as $tableName => $table) {
243 if (array_key_exists('fields', $table)) {
244 foreach ($table['fields'] as $fieldName => $field) {
9d72cede
EM
245 if (!empty($field['required']) ||
246 !empty($this->_params['fields'][$fieldName])
247 ) {
6a488035
TO
248 if (isset($field['clause'])) {
249 $select[] = $field['clause'];
250
251 // FIXME: dirty hack for setting columnHeaders
252 $this->_columnHeaders["{$field['alias']}_{$field['name']}_sum"]['type'] = CRM_Utils_Array::value('type', $field);
253 $this->_columnHeaders["{$field['alias']}_{$field['name']}_sum"]['title'] = $field['title'];
254 $this->_columnHeaders["{$field['alias']}_{$field['name']}_count"]['type'] = CRM_Utils_Array::value('type', $field);
255 $this->_columnHeaders["{$field['alias']}_{$field['name']}_count"]['title'] = $field['title'];
256 continue;
257 }
258
259 // only include statistics columns if set
260 $select[] = "{$field['dbAlias']} as {$field['alias']}_{$field['name']}";
261 $this->_columnHeaders["{$field['alias']}_{$field['name']}"]['type'] = CRM_Utils_Array::value('type', $field);
262 $this->_columnHeaders["{$field['alias']}_{$field['name']}"]['title'] = CRM_Utils_Array::value('title', $field);
a7488080 263 if (!empty($field['no_display'])) {
6a488035
TO
264 $this->_columnHeaders["{$field['alias']}_{$field['name']}"]['no_display'] = TRUE;
265 }
266 }
267 }
268 }
269 }
270
271 $this->_select = "SELECT " . implode(', ', $select) . " ";
272 }
273
74cf4551
EM
274 /**
275 * @param bool $tableCol
276 */
00be9182 277 public function groupBy($tableCol = FALSE) {
6a488035 278 $this->_groupBy = "";
9d72cede
EM
279 if (!empty($this->_params['group_bys']) &&
280 is_array($this->_params['group_bys'])
281 ) {
6a488035
TO
282 foreach ($this->_columns as $tableName => $table) {
283 if (array_key_exists('group_bys', $table)) {
284 foreach ($table['group_bys'] as $fieldName => $field) {
a7488080 285 if (!empty($this->_params['group_bys'][$fieldName])) {
6a488035
TO
286 if ($tableCol) {
287 return array($tableName, $field['alias'], $field['name']);
288 }
289 else {
290 $this->_groupBy[] = "{$field['dbAlias']}";
291 }
292 }
293 }
294 }
295 }
296
297 $this->_groupBy = "GROUP BY " . implode(', ', $this->_groupBy);
298 }
299 }
300
00be9182 301 public function from() {
6a488035
TO
302 list($fromTable, $fromAlias, $fromCol) = $this->groupBy(TRUE);
303 $from = "$fromTable $fromAlias";
304
305 if ($fromTable == 'civicrm_contact') {
306 $contriCol = "contact_id";
307 $from .= "
308LEFT JOIN civicrm_address {$this->_aliases['civicrm_address']} ON {$this->_aliases['civicrm_contact']}.id = {$this->_aliases['civicrm_address']}.contact_id
309LEFT JOIN civicrm_email {$this->_aliases['civicrm_email']}
310 ON {$this->_aliases['civicrm_contact']}.id = {$this->_aliases['civicrm_email']}.contact_id AND {$this->_aliases['civicrm_email']}.is_primary = 1
311LEFT JOIN civicrm_phone {$this->_aliases['civicrm_phone']}
312 ON {$this->_aliases['civicrm_contact']}.id = {$this->_aliases['civicrm_phone']}.contact_id AND {$this->_aliases['civicrm_phone']}.is_primary = 1";
313
314 }
4c9b6178 315 elseif ($fromTable == 'civicrm_financial_type') {
b914f4e8 316 $contriCol = "financial_type_id";
6a488035
TO
317 }
318 elseif ($fromTable == 'civicrm_contribution') {
319 $contriCol = $fromCol;
320 }
321 elseif ($fromTable == 'civicrm_address') {
322 $from .= "
323INNER JOIN civicrm_contact {$this->_aliases['civicrm_contact']} ON {$this->_aliases['civicrm_address']}.contact_id = {$this->_aliases['civicrm_contact']}.id";
324 $fromAlias = $this->_aliases['civicrm_contact'];
9d72cede 325 $fromCol = "id";
6a488035
TO
326 $contriCol = "contact_id";
327 }
328
329 $this->_from = "
330FROM $from
331LEFT JOIN civicrm_temp_civireport_repeat1 {$this->_aliases['civicrm_contribution']}1
332 ON $fromAlias.$fromCol = {$this->_aliases['civicrm_contribution']}1.$contriCol
333LEFT JOIN civicrm_temp_civireport_repeat2 {$this->_aliases['civicrm_contribution']}2
334 ON $fromAlias.$fromCol = {$this->_aliases['civicrm_contribution']}2.$contriCol";
335 }
336
74cf4551
EM
337 /**
338 * @param string $replaceAliasWith
339 *
340 * @return mixed|string
341 */
00be9182 342 public function whereContribution($replaceAliasWith = 'contribution1') {
6a488035
TO
343 $clauses = array("is_test" => "{$this->_aliases['civicrm_contribution']}.is_test = 0");
344
345 foreach ($this->_columns['civicrm_contribution']['filters'] as $fieldName => $field) {
346 $clause = NULL;
347 if (CRM_Utils_Array::value('type', $field) & CRM_Utils_Type::T_DATE) {
348 $relative = CRM_Utils_Array::value("{$fieldName}_relative", $this->_params);
9d72cede
EM
349 $from = CRM_Utils_Array::value("{$fieldName}_from", $this->_params);
350 $to = CRM_Utils_Array::value("{$fieldName}_to", $this->_params);
6a488035
TO
351
352 $clause = $this->dateClause($field['dbAlias'], $relative, $from, $to, $field['type']);
353 }
354 else {
355 $op = CRM_Utils_Array::value("{$fieldName}_op", $this->_params);
356 if ($op) {
357 $clause = $this->whereClause($field,
358 $op,
359 CRM_Utils_Array::value("{$fieldName}_value", $this->_params),
360 CRM_Utils_Array::value("{$fieldName}_min", $this->_params),
361 CRM_Utils_Array::value("{$fieldName}_max", $this->_params)
362 );
363 }
364 }
365 if (!empty($clause)) {
366 $clauses[$fieldName] = $clause;
367 }
368 }
369
370 if (!$this->_amountClauseWithAND) {
371 $amountClauseWithAND = array();
a7488080 372 if (!empty($clauses['total_amount1'])) {
6a488035
TO
373 $amountClauseWithAND[] = str_replace("{$this->_aliases['civicrm_contribution']}.total_amount",
374 "{$this->_aliases['civicrm_contribution']}1.total_amount_sum", $clauses['total_amount1']);
375 }
a7488080 376 if (!empty($clauses['total_amount2'])) {
6a488035
TO
377 $amountClauseWithAND[] = str_replace("{$this->_aliases['civicrm_contribution']}.total_amount",
378 "{$this->_aliases['civicrm_contribution']}2.total_amount_sum", $clauses['total_amount2']);
379 }
380 $this->_amountClauseWithAND = !empty($amountClauseWithAND) ? implode(' AND ', $amountClauseWithAND) : NULL;
381 }
382
383 if ($replaceAliasWith == 'contribution1') {
384 unset($clauses['receive_date2'], $clauses['total_amount2']);
385 }
386 else {
387 unset($clauses['receive_date1'], $clauses['total_amount1']);
388 }
389
84178120 390 $whereClause = !empty($clauses) ? "WHERE " . implode(' AND ', $clauses) : '';
6a488035
TO
391
392 if ($replaceAliasWith) {
393 $whereClause = str_replace($this->_aliases['civicrm_contribution'], $replaceAliasWith, $whereClause);
394 }
395
396 return $whereClause;
397 }
398
00be9182 399 public function where() {
6a488035
TO
400 if (!$this->_amountClauseWithAND) {
401 $this->_amountClauseWithAND =
402 "!({$this->_aliases['civicrm_contribution']}1.total_amount_count IS NULL AND {$this->_aliases['civicrm_contribution']}2.total_amount_count IS NULL)";
403 }
404 $clauses = array("atleast_one_amount" => $this->_amountClauseWithAND);
405
406 foreach ($this->_columns as $tableName => $table) {
9d72cede
EM
407 if (array_key_exists('filters', $table) &&
408 $tableName != 'civicrm_contribution'
409 ) {
6a488035
TO
410 foreach ($table['filters'] as $fieldName => $field) {
411 $clause = NULL;
412 $op = CRM_Utils_Array::value("{$fieldName}_op", $this->_params);
413 if ($op) {
414 $clause = $this->whereClause($field,
415 $op,
416 CRM_Utils_Array::value("{$fieldName}_value", $this->_params),
417 CRM_Utils_Array::value("{$fieldName}_min", $this->_params),
418 CRM_Utils_Array::value("{$fieldName}_max", $this->_params)
419 );
420 }
421 if (!empty($clause)) {
422 $clauses[$fieldName] = $clause;
423 }
424 }
425 }
426 }
427
84178120 428 $this->_where = !empty($clauses) ? "WHERE " . implode(' AND ', $clauses) : '';
6a488035
TO
429 }
430
74cf4551
EM
431 /**
432 * @param $fields
433 * @param $files
434 * @param $self
435 *
436 * @return array
437 */
00be9182 438 public static function formRule($fields, $files, $self) {
6a488035
TO
439
440 $errors = $checkDate = $errorCount = array();
441
442 $rules = array(
443 'id' => array(
444 'sort_name',
445 'display_name',
446 'addressee_display',
30f85891
RN
447 'contact_type',
448 'contact_sub_type',
6a488035
TO
449 'email',
450 'phone',
451 'state_province_id',
452 'country_id',
453 'city',
454 'street_address',
455 'supplemental_address_1',
456 'postal_code',
457 ),
458 'country_id' => array('country_id'),
459 'state_province_id' => array('country_id', 'state_province_id'),
460 'contribution_source' => array('contribution_source'),
b914f4e8 461 'financial_type' => array('financial_type'),
6a488035
TO
462 );
463
464 $idMapping = array(
465 'id' => ts('Contact'),
22a17535 466 'exposed_id' => ts('Contact'),
6a488035
TO
467 'country_id' => ts('Country'),
468 'state_province_id' => ts('State/Province'),
469 'contribution_source' => ts('Contribution Source'),
b914f4e8 470 'financial_type' => ts('Financial Type'),
6a488035
TO
471 'sort_name' => ts('Contact Name'),
472 'email' => ts('Email'),
473 'phone' => ts('Phone'),
474 );
475
476 if (empty($fields['group_bys'])) {
477 $errors['fields'] = ts('Please select at least one Group by field.');
478 }
479 elseif ((array_key_exists('contribution_source', $fields['group_bys']) ||
9d72cede
EM
480 array_key_exists('contribution_type', $fields['group_bys'])
481 ) &&
6a488035
TO
482 (count($fields['group_bys']) > 1)
483 ) {
484 $errors['fields'] = ts('You can not use other Group by with Financial type or Contribution source.');
485 }
486 else {
487 foreach ($fields['fields'] as $fld_id => $value) {
488 if (!($fld_id == 'total_amount1') && !($fld_id == 'total_amount2')) {
489 $found = FALSE;
490 $invlidGroups = array();
491 foreach ($fields['group_bys'] as $grp_id => $val) {
492 $validFields = $rules[$grp_id];
493 if (in_array($fld_id, $validFields)) {
494 $found = TRUE;
495 }
496 else {
497 $invlidGroups[] = $idMapping[$grp_id];
498 }
499 }
500 if (!$found) {
501 $erorrGrps = implode(',', $invlidGroups);
9d72cede 502 $tempErrors[] = ts("Do not select field %1 with Group by %2.", array(
353ffa53
TO
503 1 => $idMapping[$fld_id],
504 2 => $erorrGrps,
505 ));
6a488035
TO
506 }
507 }
508 }
509 if (!empty($tempErrors)) {
d3e86119 510 $errors['fields'] = implode("<br/>", $tempErrors);
6a488035
TO
511 }
512 }
513
8cc574cf 514 if (!empty($fields['gid_value']) && !empty($fields['group_bys'])) {
6a488035
TO
515 if (!array_key_exists('id', $fields['group_bys'])) {
516 $errors['gid_value'] = ts("Filter with Group only allow with group by Contact");
517 }
518 }
519
520 if ($fields['receive_date1_relative'] == '0') {
521 $checkDate['receive_date1']['receive_date1_from'] = $fields['receive_date1_from'];
522 $checkDate['receive_date1']['receive_date1_to'] = $fields['receive_date1_to'];
523 }
524
525 if ($fields['receive_date2_relative'] == '0') {
526 $checkDate['receive_date2']['receive_date2_from'] = $fields['receive_date2_from'];
527 $checkDate['receive_date2']['receive_date2_to'] = $fields['receive_date2_to'];
528 }
529
530 foreach ($checkDate as $date_range => $range_data) {
531 foreach ($range_data as $key => $value) {
532 if (CRM_Utils_Date::isDate($value)) {
533 $errorCount[$date_range][$key]['valid'] = 'true';
534 $errorCount[$date_range][$key]['is_empty'] = 'false';
535 }
536 else {
537 $errorCount[$date_range][$key]['valid'] = 'false';
538 $errorCount[$date_range][$key]['is_empty'] = 'true';
539 if (is_array($value)) {
540 foreach ($value as $v) {
541 if ($v) {
542 $errorCount[$date_range][$key]['is_empty'] = 'false';
543 }
544 }
545 }
546 elseif (!isset($value)) {
547 $errorCount[$date_range][$key]['is_empty'] = 'false';
548 }
549 }
550 }
551 }
552
553 $errorText = ts("Select valid date range");
554 foreach ($errorCount as $date_range => $error_data) {
555
556 if (($error_data[$date_range . '_from']['valid'] == 'false') &&
557 ($error_data[$date_range . '_to']['valid'] == 'false')
558 ) {
559
560 if (($error_data[$date_range . '_from']['is_empty'] == 'true') &&
561 ($error_data[$date_range . '_to']['is_empty'] == 'true')
562 ) {
563 $errors[$date_range . '_relative'] = $errorText;
564 }
565
566 if ($error_data[$date_range . '_from']['is_empty'] == 'false') {
567 $errors[$date_range . '_from'] = $errorText;
568 }
569
570 if ($error_data[$date_range . '_to']['is_empty'] == 'false') {
571 $errors[$date_range . '_to'] = $errorText;
572 }
573 }
574 elseif (($error_data[$date_range . '_from']['valid'] == 'true') &&
575 ($error_data[$date_range . '_to']['valid'] == 'false')
576 ) {
577 if ($error_data[$date_range . '_to']['is_empty'] == 'false') {
578 $errors[$date_range . '_to'] = $errorText;
579 }
580 }
581 elseif (($error_data[$date_range . '_from']['valid'] == 'false') &&
582 ($error_data[$date_range . '_to']['valid'] == 'true')
583 ) {
584 if ($error_data[$date_range . '_from']['is_empty'] == 'false') {
585 $errors[$date_range . '_from'] = $errorText;
586 }
587 }
588 }
589
590 return $errors;
591 }
592
74cf4551
EM
593 /**
594 * @param $rows
595 *
596 * @return array
597 */
00be9182 598 public function statistics(&$rows) {
6a488035
TO
599 $statistics = parent::statistics($rows);
600
601 //fetch contributions for both date ranges from pre-existing temp tables
602 $sql = "
603CREATE TEMPORARY TABLE civicrm_temp_civireport_repeat3
604SELECT contact_id FROM civicrm_temp_civireport_repeat1 UNION SELECT contact_id FROM civicrm_temp_civireport_repeat2;";
605 $dao = CRM_Core_DAO::executeQuery($sql);
606
607 $sql = "
2f4c2f5d 608SELECT civicrm_temp_civireport_repeat3.contact_id,
609 civicrm_temp_civireport_repeat1.total_amount_sum as contribution1_total_amount_sum,
610 civicrm_temp_civireport_repeat2.total_amount_sum as contribution2_total_amount_sum
6a488035
TO
611FROM civicrm_temp_civireport_repeat3
612LEFT JOIN civicrm_temp_civireport_repeat1
2f4c2f5d 613 ON civicrm_temp_civireport_repeat3.contact_id = civicrm_temp_civireport_repeat1.contact_id
6a488035 614LEFT JOIN civicrm_temp_civireport_repeat2
2f4c2f5d 615 ON civicrm_temp_civireport_repeat3.contact_id = civicrm_temp_civireport_repeat2.contact_id";
6a488035
TO
616 $dao = CRM_Core_DAO::executeQuery($sql);
617
618 //store contributions in array 'contact_sums' for comparison
619 $contact_sums = array();
620 while ($dao->fetch()) {
621 $contact_sums[$dao->contact_id] =
622 array(
623 'contribution1_total_amount_sum' => $dao->contribution1_total_amount_sum,
21dfd5f5 624 'contribution2_total_amount_sum' => $dao->contribution2_total_amount_sum,
6a488035
TO
625 );
626 }
627
628 $total_distinct_contacts = count($contact_sums);
629 $number_maintained = 0;
630 $number_upgraded = 0;
631 $number_downgraded = 0;
632 $number_new = 0;
633 $number_lapsed = 0;
634
635 foreach ($contact_sums as $uid => $row) {
9d72cede
EM
636 if ($row['contribution1_total_amount_sum'] &&
637 $row['contribution2_total_amount_sum']
638 ) {
639 $change = ($row['contribution1_total_amount_sum'] -
640 $row['contribution2_total_amount_sum']);
641 if ($change == 0) {
6a488035 642 $number_maintained += 1;
9d72cede
EM
643 }
644 elseif ($change > 0) {
6a488035 645 $number_upgraded += 1;
9d72cede
EM
646 }
647 elseif ($change < 0) {
6a488035 648 $number_downgraded += 1;
9d72cede 649 }
6a488035
TO
650 }
651 elseif ($row['contribution1_total_amount_sum']) {
9d72cede 652 $number_new += 1;
6a488035
TO
653 }
654 elseif ($row['contribution2_total_amount_sum']) {
9d72cede 655 $number_lapsed += 1;
6a488035
TO
656 }
657 }
658
659 //calculate percentages from numbers
660 $percent_maintained = ($number_maintained / $total_distinct_contacts) * 100;
661 $percent_upgraded = ($number_upgraded / $total_distinct_contacts) * 100;
662 $percent_downgraded = ($number_downgraded / $total_distinct_contacts) * 100;
663 $percent_new = ($number_new / $total_distinct_contacts) * 100;
664 $percent_lapsed = ($number_lapsed / $total_distinct_contacts) * 100;
665
666 //display percentages for new, lapsed, upgraded, downgraded, and maintained contributors
667 $statistics['counts']['count_new'] = array(
668 'value' => $percent_new,
669 'title' => '% New Donors',
670 );
671 $statistics['counts']['count_lapsed'] = array(
672 'value' => $percent_lapsed,
673 'title' => '% Lapsed Donors',
674 );
675 $statistics['counts']['count_upgraded'] = array(
676 'value' => $percent_upgraded,
677 'title' => '% Upgraded Donors',
678 );
679 $statistics['counts']['count_downgraded'] = array(
680 'value' => $percent_downgraded,
681 'title' => '% Downgraded Donors',
682 );
683 $statistics['counts']['count_maintained'] = array(
684 'value' => $percent_maintained,
685 'title' => '% Maintained Donors',
686 );
687
688 $select = "
689SELECT COUNT({$this->_aliases['civicrm_contribution']}1.total_amount_count ) as count,
690 SUM({$this->_aliases['civicrm_contribution']}1.total_amount_sum ) as amount,
691 ROUND(AVG({$this->_aliases['civicrm_contribution']}1.total_amount_sum), 2) as avg,
2f4c2f5d 692 COUNT({$this->_aliases['civicrm_contribution']}2.total_amount_count ) as count2,
6a488035 693 SUM({$this->_aliases['civicrm_contribution']}2.total_amount_sum ) as amount2,
625749cb 694 ROUND(AVG({$this->_aliases['civicrm_contribution']}2.total_amount_sum), 2) as avg2,
695 currency";
696 $sql = "{$select} {$this->_from} {$this->_where}
2f4c2f5d 697GROUP BY currency
625749cb 698";
6a488035
TO
699 $dao = CRM_Core_DAO::executeQuery($sql);
700
f0b6af80 701 $amount = $average = $amount2 = $average2 = array();
625749cb 702 $count = $count2 = 0;
703 while ($dao->fetch()) {
704 if ($dao->amount) {
9d72cede
EM
705 $amount[] =
706 CRM_Utils_Money::format($dao->amount, $dao->currency) . "(" .
707 $dao->count . ")";
708 $average[] = CRM_Utils_Money::format($dao->avg, $dao->currency);
625749cb 709 }
710
711 $count += $dao->count;
712 if ($dao->amount2) {
9d72cede
EM
713 $amount2[] =
714 CRM_Utils_Money::format($dao->amount2, $dao->currency) . "(" .
715 $dao->count . ")";
716 $average2[] = CRM_Utils_Money::format($dao->avg2, $dao->currency);
625749cb 717 }
718 $count2 += $dao->count2;
6a488035
TO
719 }
720
625749cb 721 $statistics['counts']['range_one_title'] = array('title' => 'Initial Date Range:');
722 $statistics['counts']['amount'] = array(
723 'value' => implode(', ', $amount),
724 'title' => 'Total Amount',
725 'type' => CRM_Utils_Type::T_STRING,
726 );
727 $statistics['counts']['count'] = array(
728 'value' => $count,
729 'title' => 'Total Donations',
730 );
731 $statistics['counts']['avg'] = array(
732 'value' => implode(', ', $average),
733 'title' => 'Average',
734 'type' => CRM_Utils_Type::T_STRING,
735 );
736 $statistics['counts']['range_two_title'] = array(
737 'title' => 'Second Date Range:',
738 );
739 $statistics['counts']['amount2'] = array(
740 'value' => implode(', ', $amount2),
741 'title' => 'Total Amount',
742 'type' => CRM_Utils_Type::T_STRING,
743 );
744 $statistics['counts']['count2'] = array(
745 'value' => $count2,
746 'title' => 'Total Donations',
747 );
748 $statistics['counts']['avg2'] = array(
749 'value' => implode(', ', $average2),
750 'title' => 'Average',
751 'type' => CRM_Utils_Type::T_STRING,
752 );
753
6a488035
TO
754 return $statistics;
755 }
756
00be9182 757 public function postProcess() {
6a488035
TO
758 $this->beginPostProcess();
759 $create = $subSelect1 = $subSelect2 = NULL;
760 list($fromTable, $fromAlias, $fromCol) = $this->groupBy(TRUE);
761 if ($fromTable == 'civicrm_contact') {
762 $contriCol = "contact_id";
763 }
764 elseif ($fromTable == 'civicrm_contribution_type') {
765 $contriCol = "contribution_type_id";
766 }
767 elseif ($fromTable == 'civicrm_contribution') {
768 $contriCol = $fromCol;
769 }
770 elseif ($fromTable == 'civicrm_address') {
771 $contriCol = "contact_id";
772 }
773 elseif ($fromTable == 'civicrm_financial_type') {
774 $contriCol = 'financial_type_id';
775 $subSelect1 = 'contribution1.contact_id,';
776 $subSelect2 = 'contribution2.contact_id,';
777 $create = 'contact_id int unsigned,';
778 }
779
780 $subWhere = $this->whereContribution();
781 $subContributionQuery1 = "
782SELECT {$subSelect1} contribution1.{$contriCol},
783 sum( contribution1.total_amount ) AS total_amount_sum,
784 count( * ) AS total_amount_count
785FROM civicrm_contribution contribution1
786{$subWhere}
787GROUP BY contribution1.{$contriCol}";
788
789 $subWhere = $this->whereContribution('contribution2');
790 $subContributionQuery2 = "
791SELECT {$subSelect2} contribution2.{$contriCol},
792 sum( contribution2.total_amount ) AS total_amount_sum,
625749cb 793 count( * ) AS total_amount_count,
794 currency
6a488035
TO
795FROM civicrm_contribution contribution2
796{$subWhere}
797GROUP BY contribution2.{$contriCol}";
798
799 $sql = "
800CREATE TEMPORARY TABLE civicrm_temp_civireport_repeat1 (
2f4c2f5d 801{$create}
6a488035
TO
802{$contriCol} int unsigned,
803total_amount_sum int,
2f4c2f5d 804total_amount_count int
6a488035
TO
805) ENGINE=HEAP DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci";
806 CRM_Core_DAO::executeQuery($sql);
807 $sql = "INSERT INTO civicrm_temp_civireport_repeat1 {$subContributionQuery1}";
808 CRM_Core_DAO::executeQuery($sql);
809
810 $sql = "
2f4c2f5d 811CREATE TEMPORARY TABLE civicrm_temp_civireport_repeat2 (
6a488035
TO
812{$create}
813{$contriCol} int unsigned,
814total_amount_sum int,
625749cb 815total_amount_count int,
816currency varchar(3)
6a488035
TO
817) ENGINE=HEAP DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci";
818 CRM_Core_DAO::executeQuery($sql);
819 $sql = "INSERT INTO civicrm_temp_civireport_repeat2 {$subContributionQuery2}";
820 CRM_Core_DAO::executeQuery($sql);
821
822 $this->select();
823 $this->from();
824 $this->where();
825 $this->groupBy();
826 $this->limit();
827
828 $count = 0;
9d72cede
EM
829 $sql = "{$this->_select} {$this->_from} {$this->_where} {$this->_groupBy} {$this->_limit}";
830 $dao = CRM_Core_DAO::executeQuery($sql);
831 $rows = array();
6a488035
TO
832 while ($dao->fetch()) {
833 foreach ($this->_columnHeaders as $key => $value) {
834 $rows[$count][$key] = $dao->$key;
835 }
836 $count++;
837 }
838
839 // FIXME: calculate % using query
840 foreach ($rows as $uid => $row) {
9d72cede
EM
841 if ($row['contribution1_total_amount_sum'] &&
842 $row['contribution2_total_amount_sum']
843 ) {
6a488035 844 $rows[$uid]['change'] = number_format((($row['contribution2_total_amount_sum'] -
9d72cede
EM
845 $row['contribution1_total_amount_sum']
846 ) * 100) /
847 ($row['contribution1_total_amount_sum']), 2
6a488035
TO
848 );
849 }
850 elseif ($row['contribution1_total_amount_sum']) {
851 $rows[$uid]['change'] = ts('Skipped Donation');
852 }
853 elseif ($row['contribution2_total_amount_sum']) {
854 $rows[$uid]['change'] = ts('New Donor');
855 }
856 if ($row['contribution1_total_amount_count']) {
9d72cede
EM
857 $rows[$uid]['contribution1_total_amount_sum'] =
858 $row['contribution1_total_amount_sum'] .
859 " ({$row['contribution1_total_amount_count']})";
6a488035
TO
860 }
861 if ($row['contribution2_total_amount_count']) {
9d72cede
EM
862 $rows[$uid]['contribution2_total_amount_sum'] =
863 $row['contribution2_total_amount_sum'] .
864 " ({$row['contribution2_total_amount_count']})";
6a488035
TO
865 }
866 }
867 $this->_columnHeaders['change'] = array(
868 'title' => '% Change',
869 'type' => CRM_Utils_Type::T_INT,
870 );
871
872 // hack to fix title
873 list($from1, $to1) = $this->getFromTo(CRM_Utils_Array::value("receive_date1_relative", $this->_params),
874 CRM_Utils_Array::value("receive_date1_from", $this->_params),
875 CRM_Utils_Array::value("receive_date1_to", $this->_params)
876 );
877 $from1 = CRM_Utils_Date::customFormat($from1, NULL, array('d'));
878 $to1 = CRM_Utils_Date::customFormat($to1, NULL, array('d'));
879
880 list($from2, $to2) = $this->getFromTo(CRM_Utils_Array::value("receive_date2_relative", $this->_params),
881 CRM_Utils_Array::value("receive_date2_from", $this->_params),
882 CRM_Utils_Array::value("receive_date2_to", $this->_params)
883 );
884 $from2 = CRM_Utils_Date::customFormat($from2, NULL, array('d'));
885 $to2 = CRM_Utils_Date::customFormat($to2, NULL, array('d'));
886
887 $this->_columnHeaders['contribution1_total_amount_sum']['title'] = "$from1 -<br/> $to1";
888 $this->_columnHeaders['contribution2_total_amount_sum']['title'] = "$from2 -<br/> $to2";
889 unset($this->_columnHeaders['contribution1_total_amount_count'],
9d72cede 890 $this->_columnHeaders['contribution2_total_amount_count']
6a488035
TO
891 );
892
893 $this->formatDisplay($rows);
894
895 // assign variables to templates
896 $this->doTemplateAssignment($rows);
897
898 $this->endPostProcess($rows);
899 }
900
74cf4551
EM
901 /**
902 * @param $rows
903 */
00be9182 904 public function alterDisplay(&$rows) {
6a488035
TO
905 // custom code to alter rows
906 list($from1, $to1) = $this->getFromTo(CRM_Utils_Array::value("receive_date1_relative", $this->_params),
907 CRM_Utils_Array::value("receive_date1_from", $this->_params),
908 CRM_Utils_Array::value("receive_date1_to", $this->_params)
909 );
910 list($from2, $to2) = $this->getFromTo(CRM_Utils_Array::value("receive_date2_relative", $this->_params),
911 CRM_Utils_Array::value("receive_date2_from", $this->_params),
912 CRM_Utils_Array::value("receive_date2_to", $this->_params)
913 );
914
915 $dateUrl = "";
916 if ($from1) {
917 $dateUrl .= "receive_date1_from={$from1}&";
918 }
919 if ($to1) {
920 $dateUrl .= "receive_date1_to={$to1}&";
921 }
922 if ($from2) {
923 $dateUrl .= "receive_date2_from={$from2}&";
924 }
925 if ($to2) {
926 $dateUrl .= "receive_date2_to={$to2}&";
927 }
928
929 foreach ($rows as $rowNum => $row) {
930 // handle country
931 if (array_key_exists('address_civireport_country_id', $row)) {
932 if ($value = $row['address_civireport_country_id']) {
933 $rows[$rowNum]['address_civireport_country_id'] = CRM_Core_PseudoConstant::country($value, FALSE);
934
935 $url = CRM_Report_Utils_Report::getNextUrl('contribute/detail',
936 "reset=1&force=1&" .
9d72cede
EM
937 "country_id_op=in&country_id_value={$value}&" .
938 "$dateUrl",
6a488035
TO
939 $this->_absoluteUrl, $this->_id, $this->_drilldownReport
940 );
941
942 $rows[$rowNum]['address_civireport_country_id_link'] = $url;
943 $rows[$rowNum]['address_civireport_country_id_hover'] = ts("View contributions for this Country.");
944 }
945 }
946
947 // handle state province
948 if (array_key_exists('address_civireport_state_province_id', $row)) {
949 if ($value = $row['address_civireport_state_province_id']) {
950 $rows[$rowNum]['address_civireport_state_province_id'] = CRM_Core_PseudoConstant::stateProvince($value, FALSE);
951
952 $url = CRM_Report_Utils_Report::getNextUrl('contribute/detail',
953 "reset=1&force=1&" .
9d72cede
EM
954 "state_province_id_op=in&state_province_id_value={$value}&" .
955 "$dateUrl",
6a488035
TO
956 $this->_absoluteUrl, $this->_id, $this->_drilldownReport
957 );
958
959 $rows[$rowNum]['address_civireport_state_province_id_link'] = $url;
960 $rows[$rowNum]['address_civireport_state_province_id_hover'] = ts("View repeatDetails for this state.");
961 }
962 }
963
964 // convert display name to links
965 if (array_key_exists('contact_civireport_sort_name', $row) &&
966 array_key_exists('contact_civireport_id', $row)
967 ) {
968 $url = CRM_Report_Utils_Report::getNextUrl('contribute/detail',
969 'reset=1&force=1&id_op=eq&id_value=' . $row['contact_civireport_id'],
970 $this->_absoluteUrl, $this->_id, $this->_drilldownReport
971 );
2f4c2f5d 972
6a488035
TO
973 $rows[$rowNum]['contact_civireport_sort_name_link'] = $url;
974 $rows[$rowNum]['contact_civireport_sort_name_hover'] = ts("View Contribution details for this contact");
975 }
976 }
977 // foreach ends
978 }
979}