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