fix version and year
[civicrm-core.git] / CRM / Report / Form / Pledge / Detail.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.7 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2016 |
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 * !!!!!!!!!!!!!!!!!!!!
31 * NB: this is named detail but behaves like a summary report.
32 * It is also accessed through the Pledge Summary link in the UI
33 * This should presumably be changed.
34 * ~ Doten
35 * !!!!!!!!!!!!!!!!!!!!
36 *
37 */
38
39 /**
40 *
41 * @package CRM
42 * @copyright CiviCRM LLC (c) 2004-2016
43 * $Id$
44 *
45 */
46 class CRM_Report_Form_Pledge_Detail extends CRM_Report_Form {
47
48 protected $_summary = NULL;
49 protected $_totalPaid = FALSE;
50 protected $_pledgeStatuses = array();
51 protected $_customGroupExtends = array(
52 'Pledge',
53 'Individual',
54 );
55
56 /**
57 */
58 /**
59 */
60 public function __construct() {
61 $this->_pledgeStatuses = CRM_Contribute_PseudoConstant::contributionStatus();
62 // Check if CiviCampaign is a) enabled and b) has active campaigns
63 $config = CRM_Core_Config::singleton();
64 $campaignEnabled = in_array("CiviCampaign", $config->enableComponents);
65 if ($campaignEnabled) {
66 $getCampaigns = CRM_Campaign_BAO_Campaign::getPermissionedCampaigns(NULL, NULL, TRUE, FALSE, TRUE);
67 $this->activeCampaigns = $getCampaigns['campaigns'];
68 asort($this->activeCampaigns);
69 }
70
71 $this->_columns = array(
72 'civicrm_contact' => array(
73 'dao' => 'CRM_Contact_DAO_Contact',
74 'fields' => array(
75 'sort_name' => array(
76 'title' => ts('Contact Name'),
77 'required' => TRUE,
78 'no_repeat' => TRUE,
79 ),
80 ),
81 'filters' => array(
82 'sort_name' => array('title' => ts('Contact Name')),
83 'id' => array('no_display' => TRUE),
84 ),
85 'grouping' => 'contact-fields',
86 ),
87 'civicrm_email' => array(
88 'dao' => 'CRM_Core_DAO_Email',
89 'fields' => array(
90 'email' => array('no_repeat' => TRUE),
91 ),
92 'grouping' => 'contact-fields',
93 ),
94 'civicrm_pledge' => array(
95 'dao' => 'CRM_Pledge_DAO_Pledge',
96 'fields' => array(
97 'id' => array(
98 'no_display' => TRUE,
99 'required' => TRUE,
100 ),
101 'contact_id' => array(
102 'no_display' => TRUE,
103 'required' => TRUE,
104 ),
105 'financial_type_id' => array(
106 'title' => ts('Financial Type'),
107 ),
108 'amount' => array(
109 'title' => ts('Pledge Amount'),
110 'required' => TRUE,
111 'type' => CRM_Utils_Type::T_MONEY,
112 ),
113 'currency' => array(
114 'required' => TRUE,
115 'no_display' => TRUE,
116 ),
117 'frequency_unit' => array(
118 'title' => ts('Frequency Unit'),
119 ),
120 'installments' => array(
121 'title' => ts('Installments'),
122 ),
123 'pledge_create_date' => array(
124 'title' => ts('Pledge Made Date'),
125 ),
126 'start_date' => array(
127 'title' => ts('Pledge Start Date'),
128 'type' => CRM_Utils_Type::T_DATE,
129 ),
130 'end_date' => array(
131 'title' => ts('Pledge End Date'),
132 'type' => CRM_Utils_Type::T_DATE,
133 ),
134 'status_id' => array(
135 'title' => ts('Pledge Status'),
136 'required' => TRUE,
137 ),
138 ),
139 'filters' => array(
140 'pledge_create_date' => array(
141 'title' => 'Pledge Made Date',
142 'operatorType' => CRM_Report_Form::OP_DATE,
143 ),
144 'pledge_amount' => array(
145 'title' => ts('Pledged Amount'),
146 'operatorType' => CRM_Report_Form::OP_INT,
147 ),
148 'currency' => array(
149 'title' => 'Currency',
150 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
151 'options' => CRM_Core_OptionGroup::values('currencies_enabled'),
152 'default' => NULL,
153 'type' => CRM_Utils_Type::T_STRING,
154 ),
155 'sid' => array(
156 'name' => 'status_id',
157 'title' => ts('Pledge Status'),
158 'type' => CRM_Utils_Type::T_INT,
159 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
160 'options' => CRM_Core_OptionGroup::values('contribution_status'),
161 ),
162 'financial_type_id' => array(
163 'title' => ts('Financial Type'),
164 'type' => CRM_Utils_Type::T_INT,
165 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
166 'options' => CRM_Contribute_PseudoConstant::financialType(),
167 ),
168
169 ),
170 ),
171 'civicrm_pledge_payment' => array(
172 'dao' => 'CRM_Pledge_DAO_PledgePayment',
173 'fields' => array(
174 'total_paid' => array(
175 'title' => ts('Total Amount Paid'),
176 'type' => CRM_Utils_Type::T_MONEY,
177 ),
178 'balance_due' => array(
179 'title' => ts('Balance Due'),
180 'default' => TRUE,
181 'type' => CRM_Utils_Type::T_MONEY,
182 ),
183 ),
184 ),
185 );
186
187 $this->_columns += $this->getAddressColumns(array('group_by' => FALSE)) + $this->getPhoneColumns();
188
189 // If we have a campaign, build out the relevant elements
190 $this->_tagFilter = TRUE;
191 if ($campaignEnabled && !empty($this->activeCampaigns)) {
192 $this->_columns['civicrm_pledge']['fields']['campaign_id'] = array(
193 'title' => 'Campaign',
194 'default' => 'false',
195 );
196 $this->_columns['civicrm_pledge']['filters']['campaign_id'] = array(
197 'title' => ts('Campaign'),
198 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
199 'options' => $this->activeCampaigns,
200 );
201 $this->_columns['civicrm_pledge']['group_bys']['campaign_id'] = array('title' => ts('Campaign'));
202
203 }
204
205 $this->_groupFilter = TRUE;
206 $this->_tagFilter = TRUE;
207 $this->_currencyColumn = 'civicrm_pledge_currency';
208 parent::__construct();
209 }
210
211 public function preProcess() {
212 parent::preProcess();
213 }
214
215 public function select() {
216 parent::select();
217 }
218
219 /**
220 * If we are retrieving total paid we need to define the inclusion of pledge_payment.
221 *
222 * @param string $tableName
223 * @param $tableKey
224 * @param string $fieldName
225 * @param $field
226 *
227 * @return bool|string
228 */
229 public function selectClause(&$tableName, $tableKey, &$fieldName, &$field) {
230 if ($fieldName == 'total_paid') {
231 $this->_totalPaid = TRUE; // add pledge_payment join
232 $this->_columnHeaders["{$tableName}_{$fieldName}"] = array(
233 'title' => $field['title'],
234 'type' => $field['type'],
235 );
236 return "COALESCE(sum({$this->_aliases[$tableName]}.actual_amount), 0) as {$tableName}_{$fieldName}";
237 }
238 if ($fieldName == 'balance_due') {
239 $cancelledStatus = array_search('Cancelled', $this->_pledgeStatuses);
240 $completedStatus = array_search('Completed', $this->_pledgeStatuses);
241 $this->_totalPaid = TRUE; // add pledge_payment join
242 $this->_columnHeaders["{$tableName}_{$fieldName}"] = $field['title'];
243 $this->_columnHeaders["{$tableName}_{$fieldName}"] = array(
244 'title' => $field['title'],
245 'type' => $field['type'],
246 );
247 return "IF({$this->_aliases['civicrm_pledge']}.status_id IN({$cancelledStatus}, $completedStatus), 0, COALESCE({$this->_aliases['civicrm_pledge']}.amount, 0) - COALESCE(sum({$this->_aliases[$tableName]}.actual_amount),0)) as {$tableName}_{$fieldName}";
248 }
249 return FALSE;
250 }
251
252 public function groupBy() {
253 parent::groupBy();
254 if (empty($this->_groupBy) && $this->_totalPaid) {
255 $this->_groupBy = " GROUP BY {$this->_aliases['civicrm_pledge']}.id, {$this->_aliases['civicrm_pledge']}.currency";
256 }
257 }
258
259 public function from() {
260 $this->_from = "
261 FROM civicrm_pledge {$this->_aliases['civicrm_pledge']}
262 LEFT JOIN civicrm_contact {$this->_aliases['civicrm_contact']}
263 ON ({$this->_aliases['civicrm_contact']}.id =
264 {$this->_aliases['civicrm_pledge']}.contact_id )
265 {$this->_aclFrom} ";
266
267 if ($this->_totalPaid) {
268 $this->_from .= "
269 LEFT JOIN civicrm_pledge_payment {$this->_aliases['civicrm_pledge_payment']} ON
270 {$this->_aliases['civicrm_pledge']}.id = {$this->_aliases['civicrm_pledge_payment']}.pledge_id
271 AND {$this->_aliases['civicrm_pledge_payment']}.status_id = 1
272 ";
273 }
274
275 $this->addPhoneFromClause();
276 $this->addAddressFromClause();
277 // include email field if email column is to be included
278 if ($this->_emailField) {
279 $this->_from .= "
280 LEFT JOIN civicrm_email {$this->_aliases['civicrm_email']}
281 ON ({$this->_aliases['civicrm_contact']}.id =
282 {$this->_aliases['civicrm_email']}.contact_id) AND
283 {$this->_aliases['civicrm_email']}.is_primary = 1\n";
284 }
285 }
286
287 /**
288 * @param $rows
289 *
290 * @return array
291 */
292 public function statistics(&$rows) {
293 $statistics = parent::statistics($rows);
294 //regenerate the from field without extra left join on pledge payments
295 $this->_totalPaid = FALSE;
296 $this->from();
297 $this->customDataFrom();
298 if (!$this->_having) {
299 $totalAmount = $average = array();
300 $count = 0;
301 $select = "
302 SELECT COUNT({$this->_aliases['civicrm_pledge']}.amount ) as count,
303 SUM({$this->_aliases['civicrm_pledge']}.amount ) as amount,
304 ROUND(AVG({$this->_aliases['civicrm_pledge']}.amount), 2) as avg,
305 {$this->_aliases['civicrm_pledge']}.currency as currency
306 ";
307
308 $group = "GROUP BY {$this->_aliases['civicrm_pledge']}.currency";
309 $sql = "{$select} {$this->_from} {$this->_where} {$group}";
310 $dao = CRM_Core_DAO::executeQuery($sql);
311 $count = $index = $totalCount = 0;
312 // this will run once per currency
313 while ($dao->fetch()) {
314 $totalAmount = CRM_Utils_Money::format($dao->amount, $dao->currency);
315 $average = CRM_Utils_Money::format($dao->avg, $dao->currency);
316 $count = $dao->count;
317 $totalCount .= $count;
318 $statistics['counts']['amount' . $index] = array(
319 'title' => ts('Total Pledged') . ' (' . $dao->currency . ')',
320 'value' => $totalAmount,
321 'type' => CRM_Utils_Type::T_STRING,
322 );
323 $statistics['counts']['avg' . $index] = array(
324 'title' => ts('Average') . ' (' . $dao->currency . ')',
325 'value' => $average,
326 'type' => CRM_Utils_Type::T_STRING,
327 );
328 $statistics['counts']['count' . $index] = array(
329 'title' => ts('Total No Pledges') . ' (' . $dao->currency . ')',
330 'value' => $count,
331 'type' => CRM_Utils_Type::T_INT,
332 );
333 $index++;
334 }
335 if ($totalCount > $count) {
336 $statistics['counts']['count' . $index] = array(
337 'title' => ts('Total No Pledges'),
338 'value' => $totalCount,
339 'type' => CRM_Utils_Type::T_INT,
340 );
341 }
342 }
343 return $statistics;
344 }
345
346 public function orderBy() {
347 $this->_orderBy = "ORDER BY {$this->_aliases['civicrm_contact']}.sort_name, {$this->_aliases['civicrm_contact']}.id";
348 }
349
350 public function where() {
351 $clauses = array();
352 foreach ($this->_columns as $tableName => $table) {
353 if (array_key_exists('filters', $table)) {
354 foreach ($table['filters'] as $fieldName => $field) {
355 $clause = NULL;
356 if (CRM_Utils_Array::value('type', $field) & CRM_Utils_Type::T_DATE) {
357 $relative = CRM_Utils_Array::value("{$fieldName}_relative", $this->_params);
358 $from = CRM_Utils_Array::value("{$fieldName}_from", $this->_params);
359 $to = CRM_Utils_Array::value("{$fieldName}_to", $this->_params);
360
361 if ($relative || $from || $to) {
362 $clause = $this->dateClause($field['name'], $relative, $from, $to, $field['type']);
363 }
364 }
365 else {
366 $op = CRM_Utils_Array::value("{$fieldName}_op", $this->_params);
367 if ($op) {
368 $clause = $this->whereClause($field,
369 $op,
370 CRM_Utils_Array::value("{$fieldName}_value",
371 $this->_params
372 ),
373 CRM_Utils_Array::value("{$fieldName}_min",
374 $this->_params
375 ),
376 CRM_Utils_Array::value("{$fieldName}_max",
377 $this->_params
378 )
379 );
380 }
381 }
382
383 if (!empty($clause)) {
384 $clauses[] = $clause;
385 }
386 }
387 }
388 }
389 if (empty($clauses)) {
390 $this->_where = "WHERE ({$this->_aliases['civicrm_pledge']}.is_test=0 ) ";
391 }
392 else {
393 $this->_where = "WHERE ({$this->_aliases['civicrm_pledge']}.is_test=0 ) AND
394 " . implode(' AND ', $clauses);
395 }
396
397 if ($this->_aclWhere) {
398 $this->_where .= " AND {$this->_aclWhere} ";
399 }
400 }
401
402 public function postProcess() {
403
404 $this->beginPostProcess();
405
406 // get the acl clauses built before we assemble the query
407 $this->buildACLClause($this->_aliases['civicrm_contact']);
408 $sql = $this->buildQuery();
409 $rows = $payment = array();
410
411 $dao = CRM_Core_DAO::executeQuery($sql);
412
413 // Set pager for the Main Query only which displays basic information
414 $this->setPager();
415 $this->assign('columnHeaders', $this->_columnHeaders);
416
417 while ($dao->fetch()) {
418 $pledgeID = $dao->civicrm_pledge_id;
419 foreach ($this->_columnHeaders as $columnHeadersKey => $columnHeadersValue) {
420 $row = array();
421 if (property_exists($dao, $columnHeadersKey)) {
422 $display[$pledgeID][$columnHeadersKey] = $dao->$columnHeadersKey;
423 }
424 }
425 $pledgeIDArray[] = $pledgeID;
426 }
427
428 // Add Special headers
429 $this->_columnHeaders['scheduled_date'] = array(
430 'type' => CRM_Utils_Type::T_DATE,
431 'title' => 'Next Payment Due',
432 );
433 $this->_columnHeaders['scheduled_amount'] = array(
434 'type' => CRM_Utils_Type::T_MONEY,
435 'title' => 'Next Payment Amount',
436 );
437 $this->_columnHeaders['status_id'] = NULL;
438
439 /*
440 * this is purely about ordering the total paid & balance due fields off to the end
441 * of the table in case custom or address fields cause them to fall in the middle
442 * (arguably the pledge amount should be moved to after these fields too)
443 *
444 */
445 $tableHeaders = array(
446 'civicrm_pledge_payment_total_paid',
447 'civicrm_pledge_payment_balance_due',
448 );
449
450 foreach ($tableHeaders as $header) {
451 //per above, unset & reset them so they move to the end
452 if (isset($this->_columnHeaders[$header])) {
453 $headervalue = $this->_columnHeaders[$header];
454 unset($this->_columnHeaders[$header]);
455 $this->_columnHeaders[$header] = $headervalue;
456 }
457 }
458
459 // To Display Payment Details of pledged amount
460 // for pledge payments In Progress
461 if (!empty($display)) {
462 $statusId = array_keys(CRM_Core_PseudoConstant::accountOptionValues("contribution_status", NULL, " AND v.name IN ('Pending', 'Overdue')"));
463 $statusId = implode(',', $statusId);
464 $sqlPayment = "
465 SELECT min(payment.scheduled_date) as scheduled_date,
466 payment.pledge_id,
467 payment.scheduled_amount,
468 pledge.contact_id
469
470 FROM civicrm_pledge_payment payment
471 LEFT JOIN civicrm_pledge pledge
472 ON pledge.id = payment.pledge_id
473
474 WHERE payment.status_id IN ({$statusId})
475
476 GROUP BY payment.pledge_id";
477
478 $daoPayment = CRM_Core_DAO::executeQuery($sqlPayment);
479
480 while ($daoPayment->fetch()) {
481 foreach ($pledgeIDArray as $key => $val) {
482 if ($val == $daoPayment->pledge_id) {
483
484 $display[$daoPayment->pledge_id]['scheduled_date'] = $daoPayment->scheduled_date;
485
486 $display[$daoPayment->pledge_id]['scheduled_amount'] = $daoPayment->scheduled_amount;
487 }
488 }
489 }
490 }
491
492 // Displaying entire data on the form
493 if (!empty($display)) {
494 foreach ($display as $key => $value) {
495 $row = array();
496 foreach ($this->_columnHeaders as $columnKey => $columnValue) {
497 if (array_key_exists($columnKey, $value)) {
498 $row[$columnKey] = !empty($value[$columnKey]) ? $value[$columnKey] : '';
499 }
500 }
501 $rows[] = $row;
502 }
503 }
504
505 unset($this->_columnHeaders['status_id']);
506 unset($this->_columnHeaders['civicrm_pledge_id']);
507 unset($this->_columnHeaders['civicrm_pledge_contact_id']);
508
509 $this->formatDisplay($rows, FALSE);
510 $this->doTemplateAssignment($rows);
511 $this->endPostProcess($rows);
512 }
513
514 /**
515 * Alter display of rows.
516 *
517 * Iterate through the rows retrieved via SQL and make changes for display purposes,
518 * such as rendering contacts as links.
519 *
520 * @param array $rows
521 * Rows generated by SQL, with an array for each row.
522 */
523 public function alterDisplay(&$rows) {
524 $entryFound = FALSE;
525 $checkList = array();
526 $display_flag = $prev_cid = $cid = 0;
527
528 foreach ($rows as $rowNum => $row) {
529 if (!empty($this->_noRepeats) && $this->_outputMode != 'csv') {
530 // don't repeat contact details if its same as the previous row
531 if (array_key_exists('civicrm_pledge_contact_id', $row)) {
532 if ($cid = $row['civicrm_pledge_contact_id']) {
533 if ($rowNum == 0) {
534 $prev_cid = $cid;
535 }
536 else {
537 if ($prev_cid == $cid) {
538 $display_flag = 1;
539 $prev_cid = $cid;
540 }
541 else {
542 $display_flag = 0;
543 $prev_cid = $cid;
544 }
545 }
546
547 if ($display_flag) {
548 foreach ($row as $colName => $colVal) {
549 if (in_array($colName, $this->_noRepeats)) {
550 unset($rows[$rowNum][$colName]);
551 }
552 }
553 }
554 $entryFound = TRUE;
555 }
556 }
557 }
558
559 // convert display name to links
560 if (array_key_exists('civicrm_contact_sort_name', $row) &&
561 array_key_exists('civicrm_pledge_contact_id', $row)
562 ) {
563 $url = CRM_Utils_System::url("civicrm/contact/view",
564 'reset=1&cid=' . $row['civicrm_pledge_contact_id'],
565 $this->_absoluteUrl
566 );
567 $rows[$rowNum]['civicrm_contact_sort_name_link'] = $url;
568 $rows[$rowNum]['civicrm_contact_sort_name_hover'] = ts("View Contact Summary for this Contact.");
569 $entryFound = TRUE;
570 }
571
572 if (array_key_exists('civicrm_pledge_financial_type_id', $row)) {
573 if ($value = $row['civicrm_pledge_financial_type_id']) {
574 $rows[$rowNum]['civicrm_pledge_financial_type_id'] = CRM_Contribute_PseudoConstant::financialType($value, FALSE);
575 }
576 $entryFound = TRUE;
577 }
578
579 //handle status id
580 if (array_key_exists('civicrm_pledge_status_id', $row)) {
581 if ($value = $row['civicrm_pledge_status_id']) {
582 $rows[$rowNum]['civicrm_pledge_status_id'] = CRM_Contribute_PseudoConstant::contributionStatus($value);
583 }
584 $entryFound = TRUE;
585 }
586
587 // If using campaigns, convert campaign_id to campaign title
588 if (array_key_exists('civicrm_pledge_campaign_id', $row)) {
589 if ($value = $row['civicrm_pledge_campaign_id']) {
590 $rows[$rowNum]['civicrm_pledge_campaign_id'] = $this->activeCampaigns[$value];
591 }
592 $entryFound = TRUE;
593 }
594
595 $entryFound = $this->alterDisplayAddressFields($row, $rows, $rowNum, 'pledge/detail', 'List all pledge(s) for this ') ? TRUE : $entryFound;
596
597 // skip looking further in rows, if first row itself doesn't
598 // have the column we need
599 if (!$entryFound) {
600 break;
601 }
602 }
603 }
604
605 }