Merge pull request #4925 from KarinG/patch-1
[civicrm-core.git] / CRM / Report / Form / Contribute / TopDonor.php
CommitLineData
6a488035 1<?php
6a488035
TO
2/*
3 +--------------------------------------------------------------------+
39de6fd5 4 | CiviCRM version 4.6 |
6a488035 5 +--------------------------------------------------------------------+
06b69b18 6 | Copyright CiviCRM LLC (c) 2004-2014 |
6a488035
TO
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
13 | |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
26*/
27
28/**
29 *
30 * @package CRM
06b69b18 31 * @copyright CiviCRM LLC (c) 2004-2014
6a488035
TO
32 * $Id$
33 *
34 */
35class CRM_Report_Form_Contribute_TopDonor extends CRM_Report_Form {
36
37 protected $_summary = NULL;
38
39 public $_drilldownReport = array('contribute/detail' => 'Link to Detail Report');
40
41 protected $_charts = array(
42 '' => 'Tabular',
43 'barChart' => 'Bar Chart',
44 'pieChart' => 'Pie Chart',
45 );
2f4c2f5d 46
74cf4551 47 /**
74cf4551 48 */
00be9182 49 public function __construct() {
6a488035 50 $this->_columns = array(
ae5ffbb7
TO
51 'civicrm_contact' => array(
52 'dao' => 'CRM_Contact_DAO_Contact',
53 'fields' => array(
54 'id' => array(
55 'no_display' => TRUE,
56 'required' => TRUE,
57 ),
58 'display_name' => array(
59 'title' => ts('Contact Name'),
60 'required' => TRUE,
61 'no_repeat' => TRUE,
62 ),
63 'first_name' => array(
64 'title' => ts('First Name'),
65 ),
66 'last_name' => array(
67 'title' => ts('Last Name'),
68 ),
69 'contact_type' => array(
70 'title' => ts('Contact Type'),
71 ),
72 'contact_sub_type' => array(
73 'title' => ts('Contact Subtype'),
7a961f19 74 ),
6a488035 75 ),
ae5ffbb7
TO
76 ),
77 );
78 $this->_columns += $this->getAddressColumns();
79 $this->_columns += array(
80 'civicrm_contribution' => array(
81 'dao' => 'CRM_Contribute_DAO_Contribution',
82 'fields' => array(
83 'total_amount' => array(
84 'title' => ts('Amount Statistics'),
85 'required' => TRUE,
86 'statistics' => array(
87 'sum' => ts('Aggregate Amount'),
88 'count' => ts('Donations'),
89 'avg' => ts('Average'),
f3bc9ff5 90 ),
6a488035 91 ),
ae5ffbb7
TO
92 'currency' => array(
93 'required' => TRUE,
94 'no_display' => TRUE,
6a488035
TO
95 ),
96 ),
ae5ffbb7
TO
97 'filters' => array(
98 'receive_date' => array(
99 'default' => 'this.year',
100 'operatorType' => CRM_Report_Form::OP_DATE,
101 ),
102 'currency' => array(
103 'title' => 'Currency',
104 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
105 'options' => CRM_Core_OptionGroup::values('currencies_enabled'),
106 'default' => NULL,
107 'type' => CRM_Utils_Type::T_STRING,
108 ),
109 'total_range' => array(
110 'title' => ts('Show no. of Top Donors'),
111 'type' => CRM_Utils_Type::T_INT,
112 'default_op' => 'eq',
113 ),
114 'financial_type_id' => array(
115 'name' => 'financial_type_id',
116 'title' => ts('Financial Type'),
117 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
118 'options' => CRM_Contribute_PseudoConstant::financialType(),
119 ),
120 'contribution_status_id' => array(
121 'title' => ts('Contribution Status'),
122 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
123 'options' => CRM_Contribute_PseudoConstant::contributionStatus(),
124 'default' => array(1),
6a488035
TO
125 ),
126 ),
ae5ffbb7
TO
127 ),
128 'civicrm_email' => array(
129 'dao' => 'CRM_Core_DAO_Email',
130 'fields' => array(
131 'email' => array(
132 'title' => ts('Email'),
133 'default' => TRUE,
134 'no_repeat' => TRUE,
135 ),
136 ),
137 'grouping' => 'email-fields',
138 ),
139 'civicrm_phone' => array(
140 'dao' => 'CRM_Core_DAO_Phone',
141 'fields' => array(
142 'phone' => array(
143 'title' => ts('Phone'),
144 'default' => TRUE,
145 'no_repeat' => TRUE,
6a488035
TO
146 ),
147 ),
ae5ffbb7
TO
148 'grouping' => 'phone-fields',
149 ),
150 );
6a488035 151
16e2e80c 152 $this->_groupFilter = TRUE;
6a488035 153 $this->_tagFilter = TRUE;
7a961f19 154 $this->_currencyColumn = 'civicrm_contribution_currency';
6a488035
TO
155 parent::__construct();
156 }
157
00be9182 158 public function preProcess() {
6a488035
TO
159 parent::preProcess();
160 }
161
00be9182 162 public function select() {
6a488035
TO
163 $select = array();
164 $this->_columnHeaders = array();
165 //Headers for Rank column
166 $this->_columnHeaders["civicrm_donor_rank"]['title'] = ts('Rank');
167 $this->_columnHeaders["civicrm_donor_rank"]['type'] = 1;
168 //$select[] ="(@rank:=@rank+1) as civicrm_donor_rank ";
169
170 foreach ($this->_columns as $tableName => $table) {
171 if (array_key_exists('fields', $table)) {
172 foreach ($table['fields'] as $fieldName => $field) {
9d72cede
EM
173 if (!empty($field['required']) ||
174 !empty($this->_params['fields'][$fieldName])
175 ) {
6a488035 176 // only include statistics columns if set
a7488080 177 if (!empty($field['statistics'])) {
6a488035
TO
178 foreach ($field['statistics'] as $stat => $label) {
179 switch (strtolower($stat)) {
180 case 'sum':
181 $select[] = "SUM({$field['dbAlias']}) as {$tableName}_{$fieldName}_{$stat}";
182 $this->_columnHeaders["{$tableName}_{$fieldName}_{$stat}"]['title'] = $label;
183 $this->_columnHeaders["{$tableName}_{$fieldName}_{$stat}"]['type'] = $field['type'];
184 $this->_statFields[] = "{$tableName}_{$fieldName}_{$stat}";
185 break;
186
187 case 'count':
188 $select[] = "COUNT({$field['dbAlias']}) as {$tableName}_{$fieldName}_{$stat}";
189 $this->_columnHeaders["{$tableName}_{$fieldName}_{$stat}"]['title'] = $label;
190 $this->_columnHeaders["{$tableName}_{$fieldName}_{$stat}"]['type'] = CRM_Utils_Type::T_INT;
191 $this->_statFields[] = "{$tableName}_{$fieldName}_{$stat}";
192 break;
193
194 case 'avg':
195 $select[] = "ROUND(AVG({$field['dbAlias']}),2) as {$tableName}_{$fieldName}_{$stat}";
196 $this->_columnHeaders["{$tableName}_{$fieldName}_{$stat}"]['type'] = $field['type'];
197 $this->_columnHeaders["{$tableName}_{$fieldName}_{$stat}"]['title'] = $label;
198 $this->_statFields[] = "{$tableName}_{$fieldName}_{$stat}";
199 break;
200 }
201 }
202 }
203 else {
204 $select[] = "{$field['dbAlias']} as {$tableName}_{$fieldName}";
21d62de7
DG
205 // $field['type'] is not always set. Use string type as default if not set.
206 $this->_columnHeaders["{$tableName}_{$fieldName}"]['type'] = isset($field['type']) ? $field['type'] : 2;
6a488035
TO
207 $this->_columnHeaders["{$tableName}_{$fieldName}"]['title'] = $field['title'];
208 }
209 }
210 }
211 }
212 }
65f9ed10 213
6a488035
TO
214 $this->_select = " SELECT * FROM ( SELECT " . implode(', ', $select) . " ";
215 }
216
74cf4551
EM
217 /**
218 * @param $fields
219 * @param $files
220 * @param $self
221 *
222 * @return array
223 */
00be9182 224 public static function formRule($fields, $files, $self) {
6a488035
TO
225 $errors = array();
226
227 $op = CRM_Utils_Array::value('total_range_op', $fields);
228 $val = CRM_Utils_Array::value('total_range_value', $fields);
229
230 if (!in_array($op, array(
9d72cede 231 'eq',
21dfd5f5 232 'lte',
9d72cede
EM
233 ))
234 ) {
6a488035
TO
235 $errors['total_range_op'] = ts("Please select 'Is equal to' OR 'Is Less than or equal to' operator");
236 }
237
238 if ($val && !CRM_Utils_Rule::positiveInteger($val)) {
239 $errors['total_range_value'] = ts("Please enter positive number");
240 }
241 return $errors;
242 }
243
00be9182 244 public function from() {
6a488035
TO
245 $this->_from = "
246 FROM civicrm_contact {$this->_aliases['civicrm_contact']} {$this->_aclFrom}
65f9ed10
E
247 INNER JOIN civicrm_contribution {$this->_aliases['civicrm_contribution']}
248 ON {$this->_aliases['civicrm_contact']}.id = {$this->_aliases['civicrm_contribution']}.contact_id AND {$this->_aliases['civicrm_contribution']}.is_test = 0
2f4c2f5d 249 LEFT JOIN civicrm_email {$this->_aliases['civicrm_email']}
250 ON {$this->_aliases['civicrm_contact']}.id = {$this->_aliases['civicrm_email']}.contact_id
6a488035 251 AND {$this->_aliases['civicrm_email']}.is_primary = 1
2f4c2f5d 252 LEFT JOIN civicrm_phone {$this->_aliases['civicrm_phone']}
6a488035 253 ON {$this->_aliases['civicrm_contact']}.id = {$this->_aliases['civicrm_phone']}.contact_id AND
2f4c2f5d 254 {$this->_aliases['civicrm_phone']}.is_primary = 1
65f9ed10 255 ";
21d62de7 256 $this->addAddressFromClause();
6a488035
TO
257 }
258
00be9182 259 public function where() {
6a488035 260 $clauses = array();
93406a82 261 $this->_tempClause = $this->_outerCluase = $this->_groupLimit = '';
6a488035
TO
262 foreach ($this->_columns as $tableName => $table) {
263 if (array_key_exists('filters', $table)) {
264 foreach ($table['filters'] as $fieldName => $field) {
265 $clause = NULL;
266 if (CRM_Utils_Array::value('type', $field) & CRM_Utils_Type::T_DATE) {
267 $relative = CRM_Utils_Array::value("{$fieldName}_relative", $this->_params);
9d72cede
EM
268 $from = CRM_Utils_Array::value("{$fieldName}_from", $this->_params);
269 $to = CRM_Utils_Array::value("{$fieldName}_to", $this->_params);
6a488035
TO
270
271 if ($relative || $from || $to) {
272 $clause = $this->dateClause($field['name'], $relative, $from, $to, $field['type']);
273 }
274 }
275 else {
276 $op = CRM_Utils_Array::value("{$fieldName}_op", $this->_params);
277 if ($op) {
278 $clause = $this->whereClause($field,
279 $op,
280 CRM_Utils_Array::value("{$fieldName}_value", $this->_params),
281 CRM_Utils_Array::value("{$fieldName}_min", $this->_params),
282 CRM_Utils_Array::value("{$fieldName}_max", $this->_params)
283 );
284 }
285 }
286
287 if (!empty($clause)) {
288 if ($fieldName == 'total_range') {
289 $value = CRM_Utils_Array::value("total_range_value", $this->_params);
290 $this->_outerCluase = " WHERE (( @rows := @rows + 1) <= {$value}) ";
93406a82 291 $this->_groupLimit = " LIMIT {$value}";
6a488035
TO
292 }
293 else {
294 $clauses[] = $clause;
295 }
296 }
297 }
298 }
299 }
300 if (empty($clauses)) {
301 $this->_where = "WHERE ( 1 ) ";
302 }
303 else {
304 $this->_where = "WHERE " . implode(' AND ', $clauses);
305 }
306
307 if ($this->_aclWhere) {
308 $this->_where .= " AND {$this->_aclWhere} ";
309 }
310 }
311
00be9182 312 public function groupBy() {
7a961f19 313 $this->_groupBy = "GROUP BY {$this->_aliases['civicrm_contact']}.id, {$this->_aliases['civicrm_contribution']}.currency";
6a488035
TO
314 }
315
00be9182 316 public function postProcess() {
6a488035
TO
317
318 $this->beginPostProcess();
319
320 // get the acl clauses built before we assemble the query
321 $this->buildACLClause($this->_aliases['civicrm_contact']);
322
323 $this->select();
324
325 $this->from();
326
327 $this->where();
328
329 $this->groupBy();
330
331 $this->limit();
332
6a488035
TO
333 //set the variable value rank, rows = 0
334 $setVariable = " SET @rows:=0, @rank=0 ";
335 CRM_Core_DAO::singleValueQuery($setVariable);
336
2f4c2f5d 337 $sql = " {$this->_select} {$this->_from} {$this->_where} {$this->_groupBy}
6a488035
TO
338 ORDER BY civicrm_contribution_total_amount_sum DESC
339 ) as abc {$this->_outerCluase} $this->_limit
340 ";
341
342 $dao = CRM_Core_DAO::executeQuery($sql);
343
344 while ($dao->fetch()) {
345 $row = array();
346 foreach ($this->_columnHeaders as $key => $value) {
347 if (property_exists($dao, $key)) {
348 $row[$key] = $dao->$key;
349 }
350 }
351 $rows[] = $row;
352 }
353 $this->formatDisplay($rows);
354
355 $this->doTemplateAssignment($rows);
356
357 $this->endPostProcess($rows);
358 }
359
74cf4551 360 /**
100fef9d 361 * @param int $groupID
74cf4551 362 */
00be9182 363 public function add2group($groupID) {
6a488035
TO
364 if (is_numeric($groupID)) {
365
2f4c2f5d 366 $sql = "
367{$this->_select} {$this->_from} {$this->_where} {$this->_groupBy}
6a488035 368ORDER BY civicrm_contribution_total_amount_sum DESC
93406a82 369) as abc {$this->_groupLimit}";
6a488035
TO
370 $dao = CRM_Core_DAO::executeQuery($sql);
371
372 $contact_ids = array();
373 // Add resulting contacts to group
374 while ($dao->fetch()) {
375 $contact_ids[$dao->civicrm_contact_id] = $dao->civicrm_contact_id;
376 }
377
378 CRM_Contact_BAO_GroupContact::addContactsToGroup($contact_ids, $groupID);
379 CRM_Core_Session::setStatus(ts("Listed contact(s) have been added to the selected group."), ts('Contacts Added'), 'success');
380 }
381 }
382
74cf4551
EM
383 /**
384 * @param int $rowCount
385 */
00be9182 386 public function limit($rowCount = CRM_Report_Form::ROW_COUNT_LIMIT) {
6a488035
TO
387 // lets do the pager if in html mode
388 $this->_limit = NULL;
74cf4551 389
dbb4a0f9
PN
390 // CRM-14115, over-ride row count if rowCount is specified in URL
391 if ($this->_dashBoardRowCount) {
392 $rowCount = $this->_dashBoardRowCount;
393 }
6a488035
TO
394 if ($this->_outputMode == 'html' || $this->_outputMode == 'group') {
395 //replace only first occurence of SELECT
396 $this->_select = preg_replace('/SELECT/', 'SELECT SQL_CALC_FOUND_ROWS ', $this->_select, 1);
397 $pageId = CRM_Utils_Request::retrieve('crmPID', 'Integer', CRM_Core_DAO::$_nullObject);
398
399 if (!$pageId && !empty($_POST) && isset($_POST['crmPID_B'])) {
400 if (!isset($_POST['PagerBottomButton'])) {
401 unset($_POST['crmPID_B']);
402 }
403 else {
9d72cede 404 $pageId = max((int) @$_POST['crmPID_B'], 1);
6a488035
TO
405 }
406 }
407
408 $pageId = $pageId ? $pageId : 1;
409 $this->set(CRM_Utils_Pager::PAGE_ID, $pageId);
410 $offset = ($pageId - 1) * $rowCount;
411
bf00d1b6
DL
412 $offset = CRM_Utils_Type::escape($offset, 'Int');
413 $rowCount = CRM_Utils_Type::escape($rowCount, 'Int');
414
6a488035
TO
415 $this->_limit = " LIMIT $offset, " . $rowCount;
416 }
417 }
418
74cf4551
EM
419 /**
420 * @param $rows
421 */
00be9182 422 public function alterDisplay(&$rows) {
6a488035
TO
423 // custom code to alter rows
424
425 $entryFound = FALSE;
426 $rank = 1;
427 if (!empty($rows)) {
428 foreach ($rows as $rowNum => $row) {
429
430 $rows[$rowNum]['civicrm_donor_rank'] = $rank++;
431 // convert display name to links
432 if (array_key_exists('civicrm_contact_display_name', $row) &&
9d72cede
EM
433 array_key_exists('civicrm_contact_id', $row) &&
434 !empty($row['civicrm_contribution_currency'])
435 ) {
6a488035 436 $url = CRM_Report_Utils_Report::getNextUrl('contribute/detail',
9d72cede
EM
437 'reset=1&force=1&id_op=eq&id_value=' . $row['civicrm_contact_id'] .
438 "&currency_value=" . $row['civicrm_contribution_currency'],
6a488035
TO
439 $this->_absoluteUrl, $this->_id, $this->_drilldownReport
440 );
441 $rows[$rowNum]['civicrm_contact_display_name_link'] = $url;
442 $entryFound = TRUE;
443 }
21d62de7 444 $entryFound = $this->alterDisplayAddressFields($row, $rows, $rowNum, 'contribute/detail', 'List all contribution(s)') ? TRUE : $entryFound;
6a488035
TO
445
446 // skip looking further in rows, if first row itself doesn't
447 // have the column we need
448 if (!$entryFound) {
449 break;
450 }
451 }
452 }
453 }
454}