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