d3510c2002e92d48927e839de283c248c09ad78e
[civicrm-core.git] / CRM / Report / Form / Contribute / Recur.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.7 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2017 |
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
31 * @copyright CiviCRM LLC (c) 2004-2017
32 * $Id$
33 *
34 */
35 class CRM_Report_Form_Contribute_Recur extends CRM_Report_Form {
36
37 /**
38 * This report has not been optimised for group filtering.
39 *
40 * The functionality for group filtering has been improved but not
41 * all reports have been adjusted to take care of it. This report has not
42 * and will run an inefficient query until fixed.
43 *
44 * CRM-19170
45 *
46 * @var bool
47 */
48 protected $groupFilterNotOptimised = TRUE;
49
50 /**
51 * Class constructor.
52 */
53 public function __construct() {
54 $this->_columns = array(
55 'civicrm_contact' => array(
56 'dao' => 'CRM_Contact_DAO_Contact',
57 'order_bys' => array(
58 'sort_name' => array(
59 'title' => ts("Last name, First name"),
60 ),
61 ),
62 'fields' => array(
63 'sort_name' => array(
64 'title' => ts('Contact Name'),
65 'no_repeat' => TRUE,
66 'default' => TRUE,
67 ),
68 'id' => array(
69 'no_display' => TRUE,
70 'required' => TRUE,
71 ),
72 ),
73 ),
74 'civicrm_email' => array(
75 'dao' => 'CRM_Core_DAO_Email',
76 'fields' => array(
77 'email' => array(
78 'title' => ts('Email'),
79 'no_repeat' => TRUE,
80 ),
81 ),
82 'grouping' => 'contact-fields',
83 ),
84 'civicrm_phone' => array(
85 'dao' => 'CRM_Core_DAO_Phone',
86 'fields' => array(
87 'phone' => array(
88 'title' => ts('Phone'),
89 'no_repeat' => TRUE,
90 ),
91 ),
92 'grouping' => 'contact-fields',
93 ),
94 'civicrm_contribution' => array(
95 'dao' => 'CRM_Contribute_DAO_Contribution',
96 'fields' => array(
97 'id' => array(
98 'no_display' => TRUE,
99 'required' => TRUE,
100 ),
101 'total_amount' => array(
102 'title' => ts('Amount Contributed to Date'),
103 'statistics' => array(
104 'sum' => ts("Total Amount Contributed"),
105 ),
106 ),
107 ),
108 ),
109 'civicrm_financial_trxn' => array(
110 'dao' => 'CRM_Financial_DAO_FinancialTrxn',
111 'fields' => array(
112 'card_type_id' => array(
113 'title' => ts('Credit Card Type ID'),
114 'dbAlias' => 'GROUP_CONCAT(financial_trxn_civireport.card_type_id SEPARATOR ",")',
115 ),
116 ),
117 'filters' => array(
118 'card_type_id' => array(
119 'title' => ts('Credit Card Type ID'),
120 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
121 'options' => CRM_Financial_DAO_FinancialTrxn::buildOptions('card_type_id'),
122 'default' => NULL,
123 'type' => CRM_Utils_Type::T_STRING,
124 ),
125 ),
126 ),
127 'civicrm_contribution_recur' => array(
128 'dao' => 'CRM_Contribute_DAO_ContributionRecur',
129 'fields' => array(
130 'id' => array(
131 'no_display' => TRUE,
132 'required' => TRUE,
133 ),
134 'currency' => array(
135 'title' => ts("Currency"),
136 'required' => TRUE,
137 'no_display' => TRUE,
138 ),
139 'contribution_status_id' => array(
140 'title' => ts('Contribution Status'),
141 ),
142 'frequency_interval' => array(
143 'title' => ts('Frequency interval'),
144 'default' => TRUE,
145 ),
146 'frequency_unit' => array(
147 'title' => ts('Frequency unit'),
148 'default' => TRUE,
149 ),
150 'amount' => array(
151 'title' => ts('Installment Amount'),
152 'default' => TRUE,
153 ),
154 'installments' => array(
155 'title' => ts('Installments'),
156 'default' => TRUE,
157 ),
158 'start_date' => array(
159 'title' => ts('Start Date'),
160 ),
161 'create_date' => array(
162 'title' => ts('Create Date'),
163 ),
164 'modified_date' => array(
165 'title' => ts('Modified Date'),
166 ),
167 'cancel_date' => array(
168 'title' => ts('Cancel Date'),
169 ),
170 'end_date' => array(
171 'title' => ts('End Date'),
172 ),
173 'next_sched_contribution_date' => array(
174 'title' => ts('Next Scheduled Contribution Date'),
175 ),
176 'failure_count' => array(
177 'title' => ts('Failure Count'),
178 ),
179 'failure_retry_date' => array(
180 'title' => ts('Failure Retry Date'),
181 ),
182 ),
183 'filters' => array(
184 'contribution_status_id' => array(
185 'title' => ts('Contribution Status'),
186 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
187 'options' => CRM_Contribute_PseudoConstant::contributionStatus(),
188 'default' => array(5),
189 'type' => CRM_Utils_Type::T_INT,
190 ),
191 'currency' => array(
192 'title' => ts('Currency'),
193 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
194 'options' => CRM_Core_OptionGroup::values('currencies_enabled'),
195 'default' => NULL,
196 'type' => CRM_Utils_Type::T_STRING,
197 ),
198 'financial_type_id' => array(
199 'title' => ts('Financial Type'),
200 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
201 'options' => CRM_Financial_BAO_FinancialType::getAvailableFinancialTypes(),
202 'type' => CRM_Utils_Type::T_INT,
203 ),
204 'frequency_unit' => array(
205 'title' => ts('Frequency Unit'),
206 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
207 'options' => CRM_Core_OptionGroup::values('recur_frequency_units'),
208 'type' => CRM_Utils_Type::T_STRING,
209 ),
210 'frequency_interval' => array(
211 'title' => ts('Frequency Interval'),
212 'type' => CRM_Utils_Type::T_INT,
213 ),
214 'amount' => array(
215 'title' => ts('Installment Amount'),
216 'type' => CRM_Utils_Type::T_MONEY,
217 ),
218 'installments' => array(
219 'title' => ts('Installments'),
220 'type' => CRM_Utils_Type::T_INT,
221 ),
222 'start_date' => array(
223 'title' => ts('Start Date'),
224 'operatorType' => CRM_Report_Form::OP_DATE,
225 'type' => CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME,
226 ),
227 'next_sched_contribution_date' => array(
228 'title' => ts('Next Scheduled Contribution Date'),
229 'operatorType' => CRM_Report_Form::OP_DATE,
230 'type' => CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME,
231 ),
232 'end_date' => array(
233 'title' => ts('End Date'),
234 'operatorType' => CRM_Report_Form::OP_DATE,
235 'type' => CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME,
236 ),
237 'modified_date' => array(
238 'title' => ts('Last Contribution Processed'),
239 'operatorType' => CRM_Report_Form::OP_DATE,
240 'type' => CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME,
241 ),
242 'calculated_end_date' => array(
243 'title' => ts('Calculated end date (either end date or date all installments will be made)'),
244 'description' => "does this work?",
245 'operatorType' => CRM_Report_Form::OP_DATE,
246 'pseudofield' => TRUE,
247 ),
248 ),
249 ),
250 );
251 $this->_currencyColumn = 'civicrm_contribution_recur_currency';
252 $this->_groupFilter = TRUE;
253 parent::__construct();
254 }
255
256 /**
257 * Get template file name.
258 *
259 * @return string
260 */
261 public function getTemplateName() {
262 return 'CRM/Report/Form.tpl';
263 }
264
265 /**
266 * Generate FROM SQL clause.
267 */
268 public function from() {
269 $this->_from = "
270 FROM civicrm_contact {$this->_aliases['civicrm_contact']}
271 INNER JOIN civicrm_contribution_recur {$this->_aliases['civicrm_contribution_recur']}
272 ON {$this->_aliases['civicrm_contact']}.id = {$this->_aliases['civicrm_contribution_recur']}.contact_id";
273 $this->_from .= "
274 LEFT JOIN civicrm_contribution {$this->_aliases['civicrm_contribution']}
275 ON {$this->_aliases['civicrm_contribution_recur']}.id = {$this->_aliases['civicrm_contribution']}.contribution_recur_id";
276 $this->_from .= "
277 LEFT JOIN civicrm_email {$this->_aliases['civicrm_email']}
278 ON ({$this->_aliases['civicrm_contact']}.id = {$this->_aliases['civicrm_email']}.contact_id AND
279 {$this->_aliases['civicrm_email']}.is_primary = 1)";
280 $this->_from .= "
281 LEFT JOIN civicrm_phone {$this->_aliases['civicrm_phone']}
282 ON ({$this->_aliases['civicrm_contact']}.id = {$this->_aliases['civicrm_phone']}.contact_id AND
283 {$this->_aliases['civicrm_phone']}.is_primary = 1)";
284
285 // for credit card type
286 $this->addFinancialTrxnFromClause();
287 }
288
289 public function groupBy() {
290 $this->_groupBy = CRM_Contact_BAO_Query::getGroupByFromSelectColumns($this->_selectClauses, "{$this->_aliases['civicrm_contribution_recur']}.id");
291 }
292
293 public function where() {
294 parent::where();
295 // Handle calculated end date. This can come from one of two sources:
296 // Either there is a specified end date for the end_date field
297 // Or, we calculate the end date based on the start date +
298 // installments * intervals using the mysql date_add function, along
299 // with the interval unit (e.g. DATE_ADD(start_date, INTERVAL 12 * 1 MONTH)
300 $date_suffixes = array('relative', 'from', 'to');
301 while (list(, $suffix) = each($date_suffixes)) {
302 $isBreak = FALSE;
303 // Check to see if the user wants to search by calculated date.
304 if (!empty($this->_params['calculated_end_date_' . $suffix])) {
305 // The calculated date field is in use - spring into action
306 // Gather values
307 $relative = CRM_Utils_Array::value("calculated_end_date_relative", $this->_params);
308 $from = CRM_Utils_Array::value("calculated_end_date_from", $this->_params);
309 $to = CRM_Utils_Array::value("calculated_end_date_to", $this->_params);
310 $end_date_db_alias = $this->_columns['civicrm_contribution_recur']['filters']['end_date']['dbAlias'];
311 $end_date_type = $this->_columns['civicrm_contribution_recur']['filters']['end_date']['type'];
312 $start_date_type = $this->_columns['civicrm_contribution_recur']['filters']['start_date']['type'];
313 $frequency_unit_db_alias = $this->_columns['civicrm_contribution_recur']['filters']['frequency_unit']['dbAlias'];
314 $frequency_interval_db_alias = $this->_columns['civicrm_contribution_recur']['filters']['frequency_interval']['dbAlias'];
315 $installments_db_alias = $this->_columns['civicrm_contribution_recur']['filters']['installments']['dbAlias'];
316 $start_date_db_alias = $this->_columns['civicrm_contribution_recur']['filters']['start_date']['dbAlias'];
317
318 // The end date clause is simple to construct
319 $end_date_clause = $this->dateClause($end_date_db_alias, $relative, $from, $to, $end_date_type, NULL, NULL);
320
321 // NOTE: For the calculation based on installment, there doesn't
322 // seem to be a way to include the interval unit (e.g. month,
323 // date, etc) as a field name - so we have to build a complex
324 // OR statement instead.
325
326 $this->_where .= 'AND (' .
327 $this->dateClause("DATE_ADD($start_date_db_alias, INTERVAL $installments_db_alias * COALESCE($frequency_interval_db_alias,1) month)",
328 $relative, $from, $to, $start_date_type, NULL, NULL);
329 $this->_where .= " AND $frequency_unit_db_alias = 'month' ) OR \n";
330
331 $this->_where .= '(' .
332 $this->dateClause("DATE_ADD($start_date_db_alias, INTERVAL $installments_db_alias * COALESCE($frequency_interval_db_alias,1) day)",
333 $relative, $from, $to, $start_date_type, NULL, NULL);
334 $this->_where .= " AND $frequency_unit_db_alias = 'day' ) OR \n";
335
336 $this->_where .= '(' .
337 $this->dateClause("DATE_ADD($start_date_db_alias, INTERVAL $installments_db_alias * COALESCE($frequency_interval_db_alias, 1) week)",
338 $relative, $from, $to, $start_date_type, NULL, NULL);
339 $this->_where .= " AND $frequency_unit_db_alias = 'week' ) OR \n";
340
341 $this->_where .= '(' .
342 $this->dateClause("DATE_ADD($start_date_db_alias, INTERVAL $installments_db_alias * COALESCE($frequency_interval_db_alias, 1) year)",
343 $relative, $from, $to, $start_date_type, NULL, NULL);
344 $this->_where .= " AND $frequency_unit_db_alias = 'year' )
345 AND (($end_date_db_alias IS NOT NULL AND $end_date_clause)
346 OR ($installments_db_alias IS NOT NULL))
347 ";
348 $isBreak = TRUE;
349 }
350 if (!empty($this->_params['modified_date_' . $suffix])) {
351 $this->_where .= " AND {$this->_aliases['civicrm_contribution_recur']}.contribution_status_id = 1";
352 $isBreak = TRUE;
353 }
354 if (!empty($isBreak)) {
355 break;
356 }
357 }
358 }
359
360
361 /**
362 * Alter display of rows.
363 *
364 * Iterate through the rows retrieved via SQL and make changes for display purposes,
365 * such as rendering contacts as links.
366 *
367 * @param array $rows
368 * Rows generated by SQL, with an array for each row.
369 */
370 public function alterDisplay(&$rows) {
371 $contributionStatus = CRM_Contribute_PseudoConstant::contributionStatus();
372 foreach ($rows as $rowNum => $row) {
373 // convert display name to links
374 if (array_key_exists('civicrm_contact_sort_name', $row) &&
375 CRM_Utils_Array::value('civicrm_contact_sort_name', $rows[$rowNum]) &&
376 array_key_exists('civicrm_contact_id', $row)
377 ) {
378 $url = CRM_Utils_System::url('civicrm/contact/view',
379 'reset=1&cid=' . $row['civicrm_contact_id'],
380 $this->_absoluteUrl
381 );
382 $rows[$rowNum]['civicrm_contact_sort_name_link'] = $url;
383 $rows[$rowNum]['civicrm_contact_sort_name_hover'] = ts('View Contact Summary for this Contact.');
384 }
385
386 // handle contribution status id
387 if ($value = CRM_Utils_Array::value('civicrm_contribution_recur_contribution_status_id', $row)) {
388 $rows[$rowNum]['civicrm_contribution_recur_contribution_status_id'] = $contributionStatus[$value];
389 }
390
391 if ($value = CRM_Utils_Array::value('civicrm_contribution_recur_amount', $row)) {
392 $rows[$rowNum]['civicrm_contribution_recur_amount'] = CRM_Utils_Money::format($rows[$rowNum]['civicrm_contribution_recur_amount'], $rows[$rowNum]['civicrm_contribution_recur_currency']);
393 }
394
395 if (!empty($row['civicrm_financial_trxn_card_type_id'])) {
396 $rows[$rowNum]['civicrm_financial_trxn_card_type_id'] = $this->getLabels($row['civicrm_financial_trxn_card_type_id'], 'CRM_Financial_DAO_FinancialTrxn', 'card_type_id');
397 }
398 }
399 }
400
401 }