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