Merge pull request #11197 from agileware/CRM-21104
[civicrm-core.git] / CRM / Report / Form / Contribute / DeferredRevenue.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 5 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2018 |
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-2018
32 * $Id$
33 *
34 */
35 class CRM_Report_Form_Contribute_DeferredRevenue extends CRM_Report_Form {
36
37 /**
38 * Holds Deferred Financial Account
39 */
40 protected $_deferredFinancialAccount = array();
41
42 /**
43 */
44 public function __construct() {
45 $this->_exposeContactID = FALSE;
46 $this->_deferredFinancialAccount = CRM_Financial_BAO_FinancialAccount::getAllDeferredFinancialAccount();
47 $this->_columns = array(
48 'civicrm_financial_trxn' => array(
49 'dao' => 'CRM_Financial_DAO_FinancialTrxn',
50 'fields' => array(
51 'status_id' => array(
52 'title' => ts('Transaction'),
53 ),
54 'trxn_date' => array(
55 'title' => ts('Transaction Date'),
56 'type' => CRM_Utils_Type::T_DATE,
57 'required' => TRUE,
58 ),
59 'total_amount' => array(
60 'title' => ts('Transaction Amount'),
61 'type' => CRM_Utils_Type::T_MONEY,
62 'required' => TRUE,
63 'dbAlias' => 'SUM(financial_trxn_1_civireport.total_amount )',
64 ),
65 ),
66 'filters' => array(
67 'trxn_date' => array(
68 'title' => ts('Transaction Date'),
69 'operatorType' => CRM_Report_Form::OP_DATE,
70 'type' => CRM_Utils_Type::T_DATE,
71 ),
72 ),
73 ),
74 'civicrm_contribution' => array(
75 'dao' => 'CRM_Contribute_DAO_Contribution',
76 'fields' => array(
77 'id' => array(
78 'title' => ts('Contribution ID'),
79 ),
80 'contribution_id' => array(
81 'title' => ts('Contribution ID'),
82 'required' => TRUE,
83 'no_display' => TRUE,
84 'dbAlias' => 'contribution_civireport.id',
85 ),
86 'contact_id' => array(
87 'title' => ts('Contact ID'),
88 ),
89 'source' => array(
90 'title' => ts('Source'),
91 ),
92 'receive_date' => array(
93 'title' => ts('Receive Date'),
94 'type' => CRM_Utils_Type::T_DATE,
95 ),
96 'cancel_date' => array(
97 'title' => ts('Cancel Date'),
98 'type' => CRM_Utils_Type::T_DATE,
99 ),
100 'revenue_recognition_date' => array(
101 'title' => ts('Revenue Recognition Date'),
102 'type' => CRM_Utils_Type::T_DATE,
103 ),
104 ),
105 'filters' => array(
106 'receive_date' => array(
107 'title' => ts('Receive Date'),
108 'operatorType' => CRM_Report_Form::OP_DATE,
109 'type' => CRM_Utils_Type::T_DATE,
110 ),
111 'cancel_date' => array(
112 'title' => ts('Cancel Date'),
113 'operatorType' => CRM_Report_Form::OP_DATE,
114 'type' => CRM_Utils_Type::T_DATE,
115 ),
116 'revenue_recognition_date' => array(
117 'title' => ts('Revenue Recognition Date'),
118 'operatorType' => CRM_Report_Form::OP_DATE,
119 'type' => CRM_Utils_Type::T_DATE,
120 ),
121 'revenue_recognition_date_toggle' => array(
122 'title' => ts("Current month's revenue?"),
123 'type' => CRM_Utils_Type::T_BOOLEAN,
124 'default' => 0,
125 'pseudofield' => TRUE,
126 ),
127 ),
128 ),
129 'civicrm_financial_account' => array(
130 'dao' => 'CRM_Financial_DAO_FinancialAccount',
131 'fields' => array(
132 'name' => array(
133 'title' => ts('Deferred Account'),
134 'required' => TRUE,
135 'no_display' => TRUE,
136 ),
137 'id' => array(
138 'title' => ts('Deferred Account ID'),
139 'required' => TRUE,
140 'no_display' => TRUE,
141 ),
142 'accounting_code' => array(
143 'title' => ts('Deferred Accounting Code'),
144 'required' => TRUE,
145 'no_display' => TRUE,
146 ),
147 ),
148 'filters' => array(
149 'id' => array(
150 'title' => ts('Deferred Financial Account'),
151 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
152 'options' => $this->_deferredFinancialAccount,
153 'type' => CRM_Utils_Type::T_INT,
154 ),
155 ),
156 ),
157 'civicrm_financial_account_1' => array(
158 'dao' => 'CRM_Financial_DAO_FinancialAccount',
159 'fields' => array(
160 'name' => array(
161 'title' => ts('Revenue Account'),
162 'required' => TRUE,
163 'no_display' => TRUE,
164 ),
165 'id' => array(
166 'title' => ts('Revenue Account ID'),
167 'required' => TRUE,
168 'no_display' => TRUE,
169 ),
170 'accounting_code' => array(
171 'title' => ts('Revenue Accounting code'),
172 'no_display' => TRUE,
173 'required' => TRUE,
174 ),
175 ),
176 ),
177 'civicrm_financial_item' => array(
178 'dao' => 'CRM_Financial_DAO_FinancialItem',
179 'fields' => array(
180 'status_id' => array(
181 'title' => ts('Status'),
182 'required' => TRUE,
183 'no_display' => TRUE,
184 ),
185 'id' => array(
186 'title' => ts('Financial Item ID'),
187 'required' => TRUE,
188 'no_display' => TRUE,
189 ),
190 'description' => array(
191 'title' => ts('Item'),
192 ),
193 ),
194 ),
195 'civicrm_financial_trxn_1' => array(
196 'dao' => 'CRM_Financial_DAO_FinancialTrxn',
197 'fields' => array(
198 'total_amount' => array(
199 'title' => ts('Deferred Transaction Amount'),
200 'type' => CRM_Utils_Type::T_MONEY,
201 'required' => TRUE,
202 'no_display' => TRUE,
203 'dbAlias' => 'GROUP_CONCAT(financial_trxn_1_civireport.total_amount)',
204 ),
205 'trxn_date' => array(
206 'title' => ts('Deferred Transaction Date'),
207 'required' => TRUE,
208 'no_display' => TRUE,
209 'dbAlias' => 'GROUP_CONCAT(financial_trxn_1_civireport.trxn_date)',
210 'type' => CRM_Utils_Type::T_DATE,
211 ),
212 ),
213 ),
214 'civicrm_contact' => array(
215 'dao' => 'CRM_Contact_DAO_Contact',
216 'fields' => array(
217 'display_name' => array(
218 'title' => ts('Contact Name'),
219 ),
220 'id' => array(
221 'title' => ts('Contact ID'),
222 'required' => TRUE,
223 'no_display' => TRUE,
224 ),
225 ),
226 ),
227 'civicrm_membership' => array(
228 'dao' => 'CRM_Member_DAO_Membership',
229 'fields' => array(
230 'start_date' => array(
231 'title' => ts('Start Date'),
232 'dbAlias' => 'IFNULL(membership_civireport.start_date, event_civireport.start_date)',
233 'type' => CRM_Utils_Type::T_DATE,
234 ),
235 'end_date' => array(
236 'title' => ts('End Date'),
237 'dbdbAlias' => 'IFNULL(membership_civireport.end_date, event_civireport.end_date)',
238 'type' => CRM_Utils_Type::T_DATE,
239 ),
240 ),
241 ),
242 'civicrm_event' => array(
243 'dao' => 'CRM_Event_DAO_Event',
244 ),
245 'civicrm_participant' => array(
246 'dao' => 'CRM_Event_DAO_Participant',
247 ),
248 'civicrm_batch' => array(
249 'dao' => 'CRM_Batch_DAO_EntityBatch',
250 'grouping' => 'contri-fields',
251 'fields' => array(
252 'batch_id' => array(
253 'title' => ts('Batch Title'),
254 'dbAlias' => "GROUP_CONCAT(DISTINCT batch_civireport.batch_id
255 ORDER BY batch_civireport.batch_id SEPARATOR ',')",
256
257 ),
258 ),
259 'filters' => array(
260 'batch_id' => array(
261 'title' => ts('Batch Title'),
262 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
263 'options' => CRM_Batch_BAO_Batch::getBatches(),
264 'type' => CRM_Utils_Type::T_INT,
265 ),
266 ),
267 ),
268 );
269 parent::__construct();
270 }
271
272 /**
273 * Pre process function.
274 *
275 * Called prior to build form.
276 */
277 public function preProcess() {
278 parent::preProcess();
279 }
280
281 /**
282 * Build from clause.
283 */
284 public function from() {
285 $deferredRelationship = key(CRM_Core_PseudoConstant::accountOptionValues('account_relationship', NULL, " AND v.name LIKE 'Deferred Revenue Account is' "));
286 $revenueRelationship = key(CRM_Core_PseudoConstant::accountOptionValues('account_relationship', NULL, " AND v.name LIKE 'Income Account is' "));
287 $this->_from = "
288 FROM civicrm_financial_item {$this->_aliases['civicrm_financial_item']}
289 INNER JOIN civicrm_entity_financial_account entity_financial_account_deferred
290 ON {$this->_aliases['civicrm_financial_item']}.financial_account_id = entity_financial_account_deferred.financial_account_id
291 AND entity_financial_account_deferred.entity_table = 'civicrm_financial_type'
292 AND entity_financial_account_deferred.account_relationship = {$deferredRelationship}
293 INNER JOIN civicrm_financial_account {$this->_aliases['civicrm_financial_account']}
294 ON entity_financial_account_deferred.financial_account_id = {$this->_aliases['civicrm_financial_account']}.id
295 INNER JOIN civicrm_entity_financial_account entity_financial_account_revenue
296 ON entity_financial_account_deferred.entity_id = entity_financial_account_revenue.entity_id
297 AND entity_financial_account_deferred.entity_table= entity_financial_account_revenue.entity_table
298 INNER JOIN civicrm_financial_account {$this->_aliases['civicrm_financial_account_1']}
299 ON entity_financial_account_revenue.financial_account_id = {$this->_aliases['civicrm_financial_account_1']}.id
300 AND {$revenueRelationship} = entity_financial_account_revenue.account_relationship
301 INNER JOIN civicrm_entity_financial_trxn entity_financial_trxn_item
302 ON entity_financial_trxn_item.entity_id = {$this->_aliases['civicrm_financial_item']}.id
303 AND entity_financial_trxn_item.entity_table = 'civicrm_financial_item'
304 INNER JOIN civicrm_financial_trxn {$this->_aliases['civicrm_financial_trxn_1']}
305 ON {$this->_aliases['civicrm_financial_trxn_1']}.to_financial_account_id = {$this->_aliases['civicrm_financial_account']}.id
306 AND {$this->_aliases['civicrm_financial_trxn_1']}.id = entity_financial_trxn_item.financial_trxn_id
307 INNER JOIN civicrm_entity_financial_trxn financial_trxn_contribution
308 ON financial_trxn_contribution.financial_trxn_id = {$this->_aliases['civicrm_financial_trxn_1']}.id
309 AND financial_trxn_contribution.entity_table = 'civicrm_contribution'
310 INNER JOIN civicrm_entity_financial_trxn entity_financial_trxn_contribution
311 ON entity_financial_trxn_contribution.entity_id = {$this->_aliases['civicrm_financial_item']}.id
312 AND entity_financial_trxn_contribution.entity_table = 'civicrm_financial_item'
313 INNER JOIN civicrm_financial_trxn {$this->_aliases['civicrm_financial_trxn']}
314 ON {$this->_aliases['civicrm_financial_trxn']}.id = entity_financial_trxn_contribution.financial_trxn_id
315 AND ({$this->_aliases['civicrm_financial_trxn']}.from_financial_account_id NOT IN (" . implode(',', array_keys($this->_deferredFinancialAccount)) . ")
316 OR {$this->_aliases['civicrm_financial_trxn']}.from_financial_account_id IS NULL)
317 INNER JOIN civicrm_contribution {$this->_aliases['civicrm_contribution']}
318 ON {$this->_aliases['civicrm_contribution']}.id = financial_trxn_contribution.entity_id
319 INNER JOIN civicrm_line_item line_item
320 ON line_item.contribution_id = {$this->_aliases['civicrm_contribution']}.id
321 AND line_item.financial_type_id = entity_financial_account_deferred.entity_id
322 LEFT JOIN civicrm_participant {$this->_aliases['civicrm_participant']}
323 ON CASE
324 WHEN line_item.entity_table = 'civicrm_participant'
325 THEN line_item.entity_id = {$this->_aliases['civicrm_participant']}.id
326 ELSE {$this->_aliases['civicrm_participant']}.id = 0
327 END
328 LEFT JOIN civicrm_event {$this->_aliases['civicrm_event']}
329 ON {$this->_aliases['civicrm_participant']}.event_id = {$this->_aliases['civicrm_event']}.id";
330
331 if ($this->isTableSelected('civicrm_contact')) {
332 $this->_from .= "
333 INNER JOIN civicrm_contact {$this->_aliases['civicrm_contact']}
334 ON {$this->_aliases['civicrm_contact']}.id = {$this->_aliases['civicrm_contribution']}.contact_id";
335 }
336
337 if ($this->isTableSelected('civicrm_membership')) {
338 $this->_from .= "
339 LEFT JOIN civicrm_membership {$this->_aliases['civicrm_membership']}
340 ON CASE
341 WHEN line_item.entity_table = 'civicrm_membership'
342 THEN line_item.entity_id = {$this->_aliases['civicrm_membership']}.id
343 ELSE {$this->_aliases['civicrm_membership']}.id = 0
344 END";
345 }
346
347 if ($this->isTableSelected('civicrm_batch')) {
348 $this->_from .= "
349 LEFT JOIN civicrm_entity_batch {$this->_aliases['civicrm_batch']}
350 ON {$this->_aliases['civicrm_batch']}.entity_id = {$this->_aliases['civicrm_financial_trxn']}.id
351 AND {$this->_aliases['civicrm_batch']}.entity_table = 'civicrm_financial_trxn'";
352 }
353 }
354
355 /**
356 * Post process function.
357 */
358 public function postProcess() {
359 $this->_noFields = TRUE;
360 parent::postProcess();
361 }
362
363 /**
364 * Set limit.
365 *
366 * @param int $rowCount
367 */
368 public function limit($rowCount = self::ROW_COUNT_LIMIT) {
369 $this->_limit = NULL;
370 }
371
372 /**
373 * Build where clause.
374 */
375 public function where() {
376 parent::where();
377 $startDate = date('Y-m-01');
378 $endDate = date('Y-m-t', strtotime(date('ymd') . '+11 month'));
379 $this->_where .= " AND {$this->_aliases['civicrm_financial_trxn_1']}.trxn_date BETWEEN '{$startDate}' AND '{$endDate}'";
380 }
381
382 /**
383 * Build group by clause.
384 */
385 public function groupBy() {
386 $this->_groupBy = "GROUP BY {$this->_aliases['civicrm_financial_account']}.id, {$this->_aliases['civicrm_financial_account_1']}.id, {$this->_aliases['civicrm_financial_item']}.id";
387 $this->_select = CRM_Contact_BAO_Query::appendAnyValueToSelect(
388 $this->_selectClauses,
389 array(
390 "{$this->_aliases['civicrm_financial_account_1']}.id",
391 "{$this->_aliases['civicrm_financial_item']}.id",
392 )
393 );
394 }
395
396 /**
397 * Modify column headers.
398 */
399 public function modifyColumnHeaders() {
400 // Re-order the columns in a custom order defined below.
401 $sortArray = array(
402 'civicrm_batch_batch_id',
403 'civicrm_financial_trxn_status_id',
404 'civicrm_financial_trxn_trxn_date',
405 'civicrm_contribution_receive_date',
406 'civicrm_contribution_cancel_date',
407 'civicrm_contribution_revenue_recognition_date',
408 'civicrm_financial_trxn_total_amount',
409 'civicrm_financial_item_description',
410 'civicrm_contribution_contact_id',
411 'civicrm_contact_display_name',
412 'civicrm_contribution_source',
413 );
414 // Only re-order selected columns.
415 $sortArray = array_flip(array_intersect_key(array_flip($sortArray), $this->_columnHeaders));
416
417 // Re-ordering.
418 $this->_columnHeaders = array_merge(array_flip($sortArray), $this->_columnHeaders);
419
420 // Add months to the columns.
421 if ($this->_params['revenue_recognition_date_toggle_value']) {
422 $this->_columnHeaders[date('M, Y', strtotime(date('Y-m-d')))] = array(
423 'title' => date('M, Y', strtotime(date('Y-m-d'))),
424 'type' => CRM_Utils_Type::T_DATE,
425 );
426 }
427 else {
428 for ($i = 0; $i < 12; $i++) {
429 $this->_columnHeaders[date('M, Y', strtotime(date('Y-m-d') . "+{$i} month"))] = array(
430 'title' => date('M, Y', strtotime(date('Y-m-d') . "+{$i} month")),
431 'type' => CRM_Utils_Type::T_DATE,
432 );
433 }
434 }
435 }
436
437 /**
438 * Build output rows.
439 *
440 * @param string $sql
441 * @param array $rows
442 */
443 public function buildRows($sql, &$rows) {
444 $dao = CRM_Core_DAO::executeQuery($sql);
445
446 // use this method to modify $this->_columnHeaders
447 $this->modifyColumnHeaders();
448
449 // Get custom date format.
450 $dateFormat = Civi::settings()->get('dateformatFinancialBatch');
451
452 if (!is_array($rows)) {
453 $rows = array();
454 }
455
456 while ($dao->fetch()) {
457 $row = array();
458 foreach ($this->_columnHeaders as $key => $value) {
459 $arraykey = $dao->civicrm_financial_account_id . '_' . $dao->civicrm_financial_account_1_id;
460
461 if (property_exists($dao, $key)) {
462 if (CRM_Utils_Array::value('type', $value) & CRM_Utils_Type::T_DATE) {
463 $row[$key] = CRM_Utils_Date::customFormat($dao->$key, $dateFormat);
464 }
465 elseif (CRM_Utils_Array::value('type', $value) & CRM_Utils_Type::T_MONEY) {
466 $row[$key] = CRM_Utils_Money::format($dao->$key);
467 }
468 else {
469 $row[$key] = $dao->$key;
470 }
471 }
472
473 $rows[$arraykey]['rows'][$dao->civicrm_financial_item_id] = $row;
474 $rows[$arraykey]['label'] = "Deferred Revenue Account: {$dao->civicrm_financial_account_name} ({$dao->civicrm_financial_account_accounting_code}), Revenue Account: {$dao->civicrm_financial_account_1_name} {$dao->civicrm_financial_account_1_accounting_code}";
475 $trxnDate = explode(',', $dao->civicrm_financial_trxn_1_trxn_date);
476 $trxnAmount = explode(',', $dao->civicrm_financial_trxn_1_total_amount);
477 foreach ($trxnDate as $key => $date) {
478 $keyDate = date('M, Y', strtotime($date));
479 $rows[$arraykey]['rows'][$dao->civicrm_financial_item_id][$keyDate] = CRM_Utils_Money::format($trxnAmount[$key]);
480 }
481 }
482 }
483 }
484
485 /**
486 * Alter display of rows.
487 *
488 * Iterate through the rows retrieved via SQL and make changes for display purposes,
489 * such as rendering contacts as links.
490 *
491 * @param array $rows
492 * Rows generated by SQL, with an array for each row.
493 */
494 public function alterDisplay(&$rows) {
495 $entryFound = FALSE;
496 $contributionStatus = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name');
497
498 foreach ($rows as &$entry) {
499 foreach ($entry['rows'] as $rowNum => &$row) {
500
501 // convert transaction status id to status name
502 if ($status = CRM_Utils_Array::value('civicrm_financial_trxn_status_id', $row)) {
503 $row['civicrm_financial_trxn_status_id'] = CRM_Core_PseudoConstant::getLabel('CRM_Core_BAO_FinancialTrxn', 'status_id', $status);
504 $entryFound = TRUE;
505 }
506
507 // convert batch id to batch title
508 if ($batchId = CRM_Utils_Array::value('civicrm_batch_batch_id', $row)) {
509 $row['civicrm_batch_batch_id'] = $this->getLabels($batchId, 'CRM_Batch_BAO_EntityBatch', 'batch_id');
510 $entryFound = TRUE;
511 }
512
513 // add hotlink for contribution
514 if ($amount = CRM_Utils_Array::value('civicrm_financial_trxn_total_amount', $row)) {
515 $contributionUrl = CRM_Utils_System::url("civicrm/contact/view/contribution",
516 'reset=1&action=view&cid=' . $row['civicrm_contact_id'] . '&id=' . $row['civicrm_contribution_contribution_id'],
517 $this->_absoluteUrl
518 );
519 $row['civicrm_financial_trxn_total_amount'] = "<a href={$contributionUrl}>{$amount}</a>";
520 if ($contributionId = CRM_Utils_Array::value('civicrm_contribution_id', $row)) {
521 $row['civicrm_contribution_id'] = "<a href={$contributionUrl}>{$contributionId}</a>";
522 }
523 $entryFound = TRUE;
524 }
525
526 // add hotlink for contact
527 if ($contactName = CRM_Utils_Array::value('civicrm_contact_display_name', $row)) {
528 $contactUrl = CRM_Utils_System::url("civicrm/contact/view",
529 'reset=1&cid=' . $row['civicrm_contact_id'],
530 $this->_absoluteUrl
531 );
532 $row['civicrm_contact_display_name'] = "<a href={$contactUrl}>{$contactName}</a>";
533 $entryFound = TRUE;
534 }
535
536 if ($contactId = CRM_Utils_Array::value('civicrm_contribution_contact_id', $row)) {
537 $contactUrl = CRM_Utils_System::url("civicrm/contact/view",
538 'reset=1&cid=' . $row['civicrm_contact_id'],
539 $this->_absoluteUrl
540 );
541 $row['civicrm_contribution_contact_id'] = "<a href={$contactUrl}>{$contactId}</a>";
542 $entryFound = TRUE;
543 }
544 }
545 }
546 }
547
548 }