3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
15 * @copyright CiviCRM LLC https://civicrm.org/licensing
17 class CRM_Report_Form_Contribute_Sybunt
extends CRM_Report_Form
{
19 protected $_charts = [
21 'barChart' => 'Bar Chart',
22 'pieChart' => 'Pie Chart',
25 protected $_customGroupExtends = [
31 public $_drilldownReport = ['contribute/detail' => 'Link to Detail Report'];
34 * This report has been optimised for group filtering.
40 protected $groupFilterNotOptimised = FALSE;
45 public function __construct() {
46 $this->_rollup
= 'WITH ROLLUP';
47 $this->_autoIncludeIndexedFieldsAsOrderBys
= 1;
50 $date = CRM_Core_SelectValues
::date('custom', NULL, $yearsInPast, $yearsInFuture);
51 $count = $date['maxYear'];
52 while ($date['minYear'] <= $count) {
53 $optionYear[$date['minYear']] = $date['minYear'];
58 'civicrm_contact' => [
59 'dao' => 'CRM_Contact_DAO_Contact',
60 'grouping' => 'contact-field',
63 'title' => ts('Donor Name'),
67 'title' => ts('First Name'),
70 'title' => ts('Middle Name'),
73 'title' => ts('Last Name'),
80 'title' => ts('Gender'),
83 'title' => ts('Birth Date'),
87 'dbAlias' => 'TIMESTAMPDIFF(YEAR, contact_civireport.birth_date, CURDATE())',
90 'title' => ts('Contact Type'),
92 'contact_sub_type' => [
93 'title' => ts('Contact Subtype'),
98 'title' => ts('Last Name, First Name'),
100 'default_weight' => '0',
101 'default_order' => 'ASC',
104 'name' => 'first_name',
105 'title' => ts('First Name'),
108 'name' => 'gender_id',
109 'title' => ts('Gender'),
112 'name' => 'birth_date',
113 'title' => ts('Birth Date'),
116 'name' => 'age_at_event',
117 'title' => ts('Age at Event'),
120 'title' => ts('Contact Type'),
122 'contact_sub_type' => [
123 'title' => ts('Contact Subtype'),
128 'title' => ts('Donor Name'),
129 'operator' => 'like',
132 'title' => ts('Contact ID'),
133 'no_display' => TRUE,
136 'title' => ts('Gender'),
137 'operatorType' => CRM_Report_Form
::OP_MULTISELECT
,
138 'options' => CRM_Core_PseudoConstant
::get('CRM_Contact_DAO_Contact', 'gender_id'),
141 'title' => ts('Birth Date'),
142 'operatorType' => CRM_Report_Form
::OP_DATE
,
145 'title' => ts('Contact Type'),
147 'contact_sub_type' => [
148 'title' => ts('Contact Subtype'),
152 'civicrm_line_item' => [
153 'dao' => 'CRM_Price_DAO_LineItem',
156 'dao' => 'CRM_Core_DAO_Email',
157 'grouping' => 'contact-field',
160 'title' => ts('Email'),
166 'dao' => 'CRM_Core_DAO_Phone',
167 'grouping' => 'contact-field',
170 'title' => ts('Phone'),
176 $this->_columns +
= $this->addAddressFields();
178 'civicrm_contribution' => [
179 'dao' => 'CRM_Contribute_DAO_Contribution',
182 'title' => ts('contactId'),
183 'no_display' => TRUE,
188 'title' => ts('Total Amount'),
189 'no_display' => TRUE,
194 'title' => ts('Year'),
195 'no_display' => TRUE,
202 'name' => 'receive_date',
203 'title' => ts('This Year'),
204 'operatorType' => CRM_Report_Form
::OP_SELECT
,
205 'options' => $optionYear,
206 'default' => date('Y'),
207 'type' => CRM_Utils_Type
::T_INT
,
209 'financial_type_id' => [
210 'title' => ts('Financial Type'),
211 'type' => CRM_Utils_Type
::T_INT
,
212 'operatorType' => CRM_Report_Form
::OP_MULTISELECT
,
213 'options' => CRM_Financial_BAO_FinancialType
::getAvailableFinancialTypes(),
215 'contribution_status_id' => [
216 'title' => ts('Contribution Status'),
217 'operatorType' => CRM_Report_Form
::OP_MULTISELECT
,
218 'options' => CRM_Contribute_BAO_Contribution
::buildOptions('contribution_status_id', 'search'),
225 'civicrm_financial_trxn' => [
226 'dao' => 'CRM_Financial_DAO_FinancialTrxn',
229 'title' => ts('Credit Card Type'),
230 'dbAlias' => 'GROUP_CONCAT(financial_trxn_civireport.card_type_id SEPARATOR ",")',
235 'title' => ts('Credit Card Type'),
236 'operatorType' => CRM_Report_Form
::OP_MULTISELECT
,
238 'options' => CRM_Financial_DAO_FinancialTrxn
::buildOptions('card_type_id'),
239 'type' => CRM_Utils_Type
::T_STRING
,
245 // If we have a campaign, build out the relevant elements
246 $this->addCampaignFields('civicrm_contribution');
248 $this->_groupFilter
= TRUE;
249 $this->_tagFilter
= TRUE;
250 parent
::__construct();
253 public function preProcess() {
254 parent
::preProcess();
257 public function select() {
259 $this->_columnHeaders
= [];
260 $current_year = $this->_params
['yid_value'];
261 $previous_year = $current_year - 1;
262 $previous_pyear = $current_year - 2;
263 $previous_ppyear = $current_year - 3;
264 $upTo_year = $current_year - 4;
266 foreach ($this->_columns
as $tableName => $table) {
267 if (array_key_exists('fields', $table)) {
268 foreach ($table['fields'] as $fieldName => $field) {
270 if (!empty($field['required']) ||
271 !empty($this->_params
['fields'][$fieldName])
273 if ($fieldName == 'total_amount') {
274 $select[] = "SUM({$field['dbAlias']}) as {$tableName}_{$fieldName}";
276 $this->_columnHeaders
["civicrm_upto_{$upTo_year}"]['type'] = $field['type'];
277 $this->_columnHeaders
["civicrm_upto_{$upTo_year}"]['title'] = ts("Up To %1", [1 => $upTo_year]);
279 $this->_columnHeaders
["year_{$previous_ppyear}"]['type'] = $field['type'];
280 $this->_columnHeaders
["year_{$previous_ppyear}"]['title'] = $previous_ppyear;
282 $this->_columnHeaders
["year_{$previous_pyear}"]['type'] = $field['type'];
283 $this->_columnHeaders
["year_{$previous_pyear}"]['title'] = $previous_pyear;
285 $this->_columnHeaders
["year_{$previous_year}"]['type'] = $field['type'];
286 $this->_columnHeaders
["year_{$previous_year}"]['title'] = $previous_year;
288 $this->_columnHeaders
["civicrm_life_time_total"]['type'] = $field['type'];
289 $this->_columnHeaders
["civicrm_life_time_total"]['title'] = ts('LifeTime');
291 elseif ($fieldName == 'receive_date') {
292 $select[] = self
::fiscalYearOffset($field['dbAlias']) .
293 " as {$tableName}_{$fieldName}";
296 $select[] = "{$field['dbAlias']} as {$tableName}_{$fieldName}";
297 $this->_columnHeaders
["{$tableName}_{$fieldName}"]['type'] = CRM_Utils_Array
::value('type', $field);
298 $this->_columnHeaders
["{$tableName}_{$fieldName}"]['title'] = $field['title'];
300 if (!empty($field['no_display'])) {
301 $this->_columnHeaders
["{$tableName}_{$fieldName}"]['no_display'] = TRUE;
307 $this->_selectClauses
= $select;
309 $this->_select
= "SELECT " . implode(', ', $select) . " ";
312 public function from() {
313 $this->setFromBase('civicrm_contribution', 'contact_id');
315 INNER JOIN civicrm_contact {$this->_aliases['civicrm_contact']}
316 ON {$this->_aliases['civicrm_contact']}.id = {$this->_aliases['civicrm_contribution']}.contact_id
319 $this->joinPhoneFromContact();
320 $this->joinEmailFromContact();
322 // for credit card type
323 $this->addFinancialTrxnFromClause();
325 $this->joinAddressFromContact();
328 public function where() {
329 $this->_statusClause
= "";
330 $clauses = [$this->_aliases
['civicrm_contribution'] . '.is_test = 0'];
331 foreach ($this->_columns
as $tableName => $table) {
332 if (array_key_exists('filters', $table)) {
333 foreach ($table['filters'] as $fieldName => $field) {
335 if ($fieldName == 'yid') {
336 $clause = "contribution_civireport.contact_id NOT IN
337 (SELECT distinct cont.id FROM civicrm_contact cont, civicrm_contribution contri
338 WHERE cont.id = contri.contact_id AND " .
339 self
::fiscalYearOffset('contri.receive_date') .
340 " = {$this->_params['yid_value']} AND contri.is_test = 0 )";
342 elseif (CRM_Utils_Array
::value('type', $field) & CRM_Utils_Type
::T_DATE
344 $relative = CRM_Utils_Array
::value("{$fieldName}_relative", $this->_params
);
345 $from = CRM_Utils_Array
::value("{$fieldName}_from", $this->_params
);
346 $to = CRM_Utils_Array
::value("{$fieldName}_to", $this->_params
);
348 if ($relative ||
$from ||
$to) {
349 $clause = $this->dateClause($field['name'], $relative, $from, $to, $field['type']);
353 $op = CRM_Utils_Array
::value("{$fieldName}_op", $this->_params
);
355 $clause = $this->whereClause($field,
357 CRM_Utils_Array
::value("{$fieldName}_value", $this->_params
),
358 CRM_Utils_Array
::value("{$fieldName}_min", $this->_params
),
359 CRM_Utils_Array
::value("{$fieldName}_max", $this->_params
)
361 if (($fieldName == 'contribution_status_id' ||
362 $fieldName == 'financial_type_id') && !empty($clause)
364 $this->_statusClause
.= " AND " . $clause;
369 if (!empty($clause)) {
370 $clauses[] = $clause;
376 $this->_where
= "WHERE " . implode(' AND ', $clauses);
378 if ($this->_aclWhere
) {
379 $this->_where
.= " AND {$this->_aclWhere} ";
383 public function groupBy() {
384 $this->assign('chartSupported', TRUE);
385 $fiscalYearOffset = self
::fiscalYearOffset("{$this->_aliases['civicrm_contribution']}.receive_date");
386 $this->_groupBy
= "GROUP BY {$this->_aliases['civicrm_contribution']}.contact_id, {$fiscalYearOffset}";
387 $this->_select
= CRM_Contact_BAO_Query
::appendAnyValueToSelect($this->_selectClauses
, ["{$this->_aliases['civicrm_contribution']}.contact_id", $fiscalYearOffset]);
388 $this->_groupBy
.= " {$this->_rollup}";
396 public function statistics(&$rows) {
397 $statistics = parent
::statistics($rows);
402 SUM({$this->_aliases['civicrm_contribution']}.total_amount ) as amount ";
404 $sql = "{$select} {$this->_from} {$this->_where}";
405 $dao = CRM_Core_DAO
::executeQuery($sql);
407 $statistics['counts']['amount'] = [
408 'value' => $dao->amount
,
409 'title' => ts('Total LifeTime'),
410 'type' => CRM_Utils_Type
::T_MONEY
,
417 public function postProcess() {
418 // get ready with post process params
419 $this->beginPostProcess();
420 $this->buildACLClause($this->_aliases
['civicrm_contact']);
423 $rows = $contactIds = [];
424 if (empty($this->_params
['charts'])) {
426 $getContacts = "SELECT SQL_CALC_FOUND_ROWS {$this->_aliases['civicrm_contact']}.id as cid {$this->_from} {$this->_where} GROUP BY {$this->_aliases['civicrm_contact']}.id {$this->_limit}";
428 $dao = CRM_Core_DAO
::executeQuery($getContacts);
430 while ($dao->fetch()) {
431 $contactIds[] = $dao->cid
;
436 if (!empty($contactIds) ||
!empty($this->_params
['charts'])) {
437 if (!empty($this->_params
['charts'])) {
438 $sql = "{$this->_select} {$this->_from} {$this->_where} {$this->_groupBy}";
442 "{$this->_select} {$this->_from} WHERE {$this->_aliases['civicrm_contact']}.id IN (" .
443 implode(',', $contactIds) .
444 ") AND {$this->_aliases['civicrm_contribution']}.is_test = 0 {$this->_statusClause} {$this->_groupBy} ";
447 $current_year = $this->_params
['yid_value'];
448 $previous_year = $current_year - 1;
449 $previous_pyear = $current_year - 2;
450 $previous_ppyear = $current_year - 3;
451 $upTo_year = $current_year - 4;
454 $dao = CRM_Core_DAO
::executeQuery($sql);
455 $contributionSum = 0;
457 while ($dao->fetch()) {
458 if (!$dao->civicrm_contribution_contact_id
) {
462 foreach ($this->_columnHeaders
as $key => $value) {
463 if (property_exists($dao, $key)) {
464 $rows[$dao->civicrm_contribution_contact_id
][$key] = $dao->$key;
467 if ($dao->civicrm_contribution_receive_date
) {
468 if ($dao->civicrm_contribution_receive_date
> $upTo_year) {
469 $contributionSum +
= $dao->civicrm_contribution_total_amount
;
470 $rows[$dao->civicrm_contribution_contact_id
]['year_' . $dao->civicrm_contribution_receive_date
] = $dao->civicrm_contribution_total_amount
;
474 $rows[$dao->civicrm_contribution_contact_id
]['civicrm_life_time_total'] = $dao->civicrm_contribution_total_amount
;
475 if (($dao->civicrm_contribution_total_amount
- $contributionSum) > 0
477 $rows[$dao->civicrm_contribution_contact_id
]["civicrm_upto_{$upTo_year}"]
478 = $dao->civicrm_contribution_total_amount
- $contributionSum;
480 $contributionSum = 0;
484 // format result set.
485 $this->formatDisplay($rows, FALSE);
487 // assign variables to templates
488 $this->doTemplateAssignment($rows);
490 // do print / pdf / instance stuff if needed
491 $this->endPostProcess($rows);
497 public function buildChart(&$rows) {
500 $current_year = $this->_params
['yid_value'];
501 $previous_year = $current_year - 1;
502 $previous_two_year = $current_year - 2;
503 $previous_three_year = $current_year - 3;
504 $upto = $current_year - 4;
506 $interval[$previous_year] = $previous_year;
507 $interval[$previous_two_year] = $previous_two_year;
508 $interval[$previous_three_year] = $previous_three_year;
509 $interval["upto_{$upto}"] = "Up To {$upto}";
511 foreach ($rows as $key => $row) {
512 $display["upto_{$upto}"]
513 = CRM_Utils_Array
::value("upto_{$upto}", $display) + CRM_Utils_Array
::value("civicrm_upto_{$upto}", $row);
514 $display[$previous_year]
515 = CRM_Utils_Array
::value($previous_year, $display) + CRM_Utils_Array
::value($previous_year, $row);
516 $display[$previous_two_year]
517 = CRM_Utils_Array
::value($previous_two_year, $display) + CRM_Utils_Array
::value($previous_two_year, $row);
518 $display[$previous_three_year]
519 = CRM_Utils_Array
::value($previous_three_year, $display) + CRM_Utils_Array
::value($previous_three_year, $row);
522 $graphRows['value'] = $display;
523 $config = CRM_Core_Config
::Singleton();
525 'legend' => ts('Sybunt Report'),
526 'xname' => ts('Year'),
527 'yname' => ts('Amount (%1)', [1 => $config->defaultCurrency
]),
529 if ($this->_params
['charts']) {
531 CRM_Utils_Chart
::reportChart($graphRows, $this->_params
['charts'], $interval, $chartInfo);
532 $this->assign('chartType', $this->_params
['charts']);
537 * Alter display of rows.
539 * Iterate through the rows retrieved via SQL and make changes for display purposes,
540 * such as rendering contacts as links.
543 * Rows generated by SQL, with an array for each row.
545 public function alterDisplay(&$rows) {
548 foreach ($rows as $rowNum => $row) {
549 //Convert Display name into link
550 if (array_key_exists('civicrm_contact_sort_name', $row) &&
551 array_key_exists('civicrm_contribution_contact_id', $row)
553 $url = CRM_Report_Utils_Report
::getNextUrl('contribute/detail',
554 'reset=1&force=1&id_op=eq&id_value=' .
555 $row['civicrm_contribution_contact_id'],
556 $this->_absoluteUrl
, $this->_id
, $this->_drilldownReport
558 $rows[$rowNum]['civicrm_contact_sort_name_link'] = $url;
559 $rows[$rowNum]['civicrm_contact_sort_name_hover'] = ts("View Contribution Details for this Contact.");
563 // convert campaign_id to campaign title
564 if (array_key_exists('civicrm_contribution_campaign_id', $row)) {
565 if ($value = $row['civicrm_contribution_campaign_id']) {
566 $rows[$rowNum]['civicrm_contribution_campaign_id'] = $this->campaigns
[$value];
571 $entryFound = $this->alterDisplayAddressFields($row, $rows, $rowNum, 'contribute/detail', 'List all contribution(s)') ?
TRUE : $entryFound;
572 $entryFound = $this->alterDisplayContactFields($row, $rows, $rowNum, NULL, 'List all contribution(s)') ?
TRUE : $entryFound;
573 if (!empty($row['civicrm_financial_trxn_card_type_id'])) {
574 $rows[$rowNum]['civicrm_financial_trxn_card_type_id'] = $this->getLabels($row['civicrm_financial_trxn_card_type_id'], 'CRM_Financial_DAO_FinancialTrxn', 'card_type_id');
578 // skip looking further in rows, if first row itself doesn't
579 // have the column we need
587 * Override "This Year" $op options
588 * @param string $type
589 * @param null $fieldName
593 public function getOperationPair($type = "string", $fieldName = NULL) {
594 if ($fieldName == 'yid') {
596 'calendar' => ts('Is Calendar Year'),
597 'fiscal' => ts('Fiscal Year Starting'),
600 return parent
::getOperationPair($type, $fieldName);