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 +--------------------------------------------------------------------+
14 * @copyright CiviCRM LLC https://civicrm.org/licensing
18 * Class to render contribution search results.
20 class CRM_Contribute_Selector_Search
extends CRM_Core_Selector_Base
implements CRM_Core_Selector_API
{
23 * Array of action links.
27 public static $_links = NULL;
30 * We use desc to remind us what that column is, name is used in the tpl
34 public static $_columnHeaders;
37 * Properties of contact we're interested in displaying
40 public static $_properties = [
48 'contribution_source',
51 'contribution_status_id',
52 'contribution_status',
53 'contribution_cancel_date',
57 'contribution_recur_id',
61 'contribution_campaign_id',
62 'contribution_soft_credit_name',
63 'contribution_soft_credit_contact_id',
64 'contribution_soft_credit_amount',
65 'contribution_soft_credit_type',
69 * Are we restricting ourselves to a single contact
73 protected $_single = FALSE;
76 * Are we restricting ourselves to a single contact
80 protected $_limit = NULL;
83 * What context are we being invoked from
87 protected $_context = NULL;
90 * What component context are we being invoked from
94 protected $_compContext = NULL;
97 * QueryParams is the array returned by exportValues called on
98 * the HTML_QuickForm_Controller for that page.
102 public $_queryParams;
105 * Represent the type of selector
112 * The additional clause that we restrict the search with
116 protected $_contributionClause = NULL;
121 * @var CRM_Contact_BAO_Query
125 protected $_includeSoftCredits = FALSE;
130 * @param array $queryParams
131 * Array of parameters for query.
132 * @param \const|int $action - action of search basic or advanced.
133 * @param string $contributionClause
134 * If the caller wants to further restrict the search (used in contributions).
135 * @param bool $single
136 * Are we dealing only with one contact?.
138 * How many contributions do we want returned.
140 * @param string $context
141 * @param null $compContext
143 * @return CRM_Contribute_Selector_Search
145 public function __construct(
147 $action = CRM_Core_Action
::NONE
,
148 $contributionClause = NULL,
155 // submitted form values
156 $this->_queryParams
= &$queryParams;
158 $this->_single
= $single;
159 $this->_limit
= $limit;
160 $this->_context
= $context;
161 $this->_compContext
= $compContext;
163 $this->_contributionClause
= $contributionClause;
166 $this->_action
= $action;
167 $returnProperties = CRM_Contribute_BAO_Query
::selectorReturnProperties($this->_queryParams
);
168 $this->_includeSoftCredits
= CRM_Contribute_BAO_Query
::isSoftCreditOptionEnabled($this->_queryParams
);
169 $this->_queryParams
[] = ['contribution_id', '!=', 0, 0, 0];
170 $this->_query
= new CRM_Contact_BAO_Query(
174 CRM_Contact_BAO_Query
::MODE_CONTRIBUTE
176 // @todo the function CRM_Contribute_BAO_Query::isSoftCreditOptionEnabled should handle this
177 // can we remove? if not why not?
178 if ($this->_includeSoftCredits
) {
179 $this->_query
->_rowCountClause
= " count(civicrm_contribution.id)";
180 $this->_query
->_groupByComponentClause
= " GROUP BY contribution_search_scredit_combined.id, contribution_search_scredit_combined.contact_id, contribution_search_scredit_combined.scredit_id ";
183 $this->_query
->_distinctComponentClause
= " civicrm_contribution.id";
184 $this->_query
->_groupByComponentClause
= " GROUP BY civicrm_contribution.id ";
189 * This method returns the links that are given for each search row.
190 * currently the links added for each row are
195 * @param int $componentId
196 * @param null $componentAction
198 * @param null $compContext
202 public static function &links($componentId = NULL, $componentAction = NULL, $key = NULL, $compContext = NULL) {
205 $extraParams = "&compId={$componentId}&compAction={$componentAction}";
208 $extraParams .= "&compContext={$compContext}";
211 $extraParams .= "&key={$key}";
214 if (!(self
::$_links)) {
216 CRM_Core_Action
::VIEW
=> [
217 'name' => ts('View'),
218 'url' => 'civicrm/contact/view/contribution',
219 'qs' => "reset=1&id=%%id%%&cid=%%cid%%&action=view&context=%%cxt%%&selectedChild=contribute{$extraParams}",
220 'title' => ts('View Contribution'),
222 CRM_Core_Action
::UPDATE
=> [
223 'name' => ts('Edit'),
224 'url' => 'civicrm/contact/view/contribution',
225 'qs' => "reset=1&action=update&id=%%id%%&cid=%%cid%%&context=%%cxt%%{$extraParams}",
226 'title' => ts('Edit Contribution'),
228 CRM_Core_Action
::DELETE
=> [
229 'name' => ts('Delete'),
230 'url' => 'civicrm/contact/view/contribution',
231 'qs' => "reset=1&action=delete&id=%%id%%&cid=%%cid%%&context=%%cxt%%{$extraParams}",
232 'title' => ts('Delete Contribution'),
236 return self
::$_links;
240 * Getter for array of the parameters required for creating pager.
243 * @param array $params
245 public function getPagerParams($action, &$params) {
246 $params['status'] = ts('Contribution') . ' %%StatusMessage%%';
247 $params['csvString'] = NULL;
249 $params['rowCount'] = $this->_limit
;
252 $params['rowCount'] = Civi
::settings()->get('default_pager_size');
255 $params['buttonTop'] = 'PagerTopButton';
256 $params['buttonBottom'] = 'PagerBottomButton';
260 * Returns total number of rows for the query.
262 * @param string $action
265 * Total number of rows
267 public function getTotalCount($action) {
268 return $this->_query
->searchQuery(0, 0, NULL,
272 $this->_contributionClause
277 * Returns all the rows in the given offset and rowCount.
279 * @param string $action
280 * The action being performed.
282 * The row number to start from.
283 * @param int $rowCount
284 * The number of rows to return.
285 * @param string $sort
286 * The sql string that describes the sort order.
287 * @param string $output
288 * What should the result set include (web/email/csv).
291 * the total number of rows for this action
293 public function &getRows($action, $offset, $rowCount, $sort, $output = NULL) {
294 if ($this->_includeSoftCredits
) {
295 // especial sort order when rows include soft credits
296 $sort = $sort->orderBy() . ", civicrm_contribution.id, civicrm_contribution_soft.id";
298 $result = $this->_query
->searchQuery($offset, $rowCount, $sort,
302 $this->_contributionClause
304 // process the result of the query
307 //CRM-4418 check for view/edit/delete
308 $permissions = [CRM_Core_Permission
::VIEW
];
309 if (CRM_Core_Permission
::check('edit contributions')) {
310 $permissions[] = CRM_Core_Permission
::EDIT
;
312 if (CRM_Core_Permission
::check('delete in CiviContribute')) {
313 $permissions[] = CRM_Core_Permission
::DELETE
;
315 $mask = CRM_Core_Action
::mask($permissions);
317 $qfKey = $this->_key
;
318 $componentId = $componentContext = NULL;
319 if ($this->_context
!= 'contribute') {
320 // @todo explain the significance of context & why we do not get these i that context.
321 $qfKey = CRM_Utils_Request
::retrieve('key', 'String');
322 $componentId = CRM_Utils_Request
::retrieve('id', 'Positive');
323 $componentAction = CRM_Utils_Request
::retrieve('action', 'String');
324 $componentContext = CRM_Utils_Request
::retrieve('compContext', 'String');
326 if (!$componentContext &&
329 // @todo explain when this condition might occur.
330 $componentContext = $this->_compContext
;
331 $qfKey = CRM_Utils_Request
::retrieve('qfKey', 'String', CRM_Core_DAO
::$_nullObject, NULL, FALSE, 'REQUEST');
333 // CRM-17628 for some reason qfKey is not always set when searching from contribution search.
334 // as a result if the edit link is opened using right-click + open in new tab
335 // then the browser is not returned to the search results on save.
336 // This is an effort to getting the qfKey without, sadly, understanding the intent of those who came before me.
338 $qfKey = CRM_Utils_Request
::retrieve('qfKey', 'String', CRM_Core_DAO
::$_nullObject, NULL, FALSE, 'REQUEST');
342 // get all contribution status
343 $contributionStatuses = CRM_Core_OptionGroup
::values('contribution_status',
344 FALSE, FALSE, FALSE, NULL, 'name', FALSE
348 $allCampaigns = CRM_Campaign_BAO_Campaign
::getCampaigns(NULL, NULL, FALSE, FALSE, FALSE, TRUE);
350 while ($result->fetch()) {
351 $this->_query
->convertToPseudoNames($result);
352 $links = self
::links($componentId,
358 $checkLineItem = FALSE;
359 // Set defaults to empty to prevent e-notices.
360 $row = ['amount_level' => ''];
361 // Now check for lineItems
362 if (CRM_Financial_BAO_FinancialType
::isACLFinancialTypeStatus()) {
363 $lineItems = CRM_Price_BAO_LineItem
::getLineItemsByContributionID($result->id
);
364 foreach ($lineItems as $items) {
365 if (!CRM_Core_Permission
::check('view contributions of type ' . CRM_Contribute_PseudoConstant
::financialType($items['financial_type_id']))) {
366 $checkLineItem = TRUE;
369 if (!CRM_Core_Permission
::check('edit contributions of type ' . CRM_Contribute_PseudoConstant
::financialType($items['financial_type_id']))) {
370 unset($links[CRM_Core_Action
::UPDATE
]);
372 if (!CRM_Core_Permission
::check('delete contributions of type ' . CRM_Contribute_PseudoConstant
::financialType($items['financial_type_id']))) {
373 unset($links[CRM_Core_Action
::DELETE
]);
376 if ($checkLineItem) {
379 if (!CRM_Core_Permission
::check('edit contributions of type ' . CRM_Contribute_PseudoConstant
::financialType($result->financial_type_id
))) {
380 unset($links[CRM_Core_Action
::UPDATE
]);
382 if (!CRM_Core_Permission
::check('delete contributions of type ' . CRM_Contribute_PseudoConstant
::financialType($result->financial_type_id
))) {
383 unset($links[CRM_Core_Action
::DELETE
]);
386 // the columns we are interested in
387 foreach (self
::$_properties as $property) {
388 if (property_exists($result, $property)) {
389 $row[$property] = $result->$property;
393 //carry campaign on selectors.
394 // @todo - I can't find any evidence that 'carrying' the campaign on selectors actually
395 // results in it being displayed anywhere so why do we do this???
396 $row['campaign'] = $allCampaigns[$result->contribution_campaign_id
] ??
NULL;
397 $row['campaign_id'] = $result->contribution_campaign_id
;
399 // add contribution status name
400 $row['contribution_status_name'] = CRM_Utils_Array
::value($row['contribution_status_id'],
401 $contributionStatuses
405 if ($result->is_pay_later
&& CRM_Utils_Array
::value('contribution_status_name', $row) == 'Pending') {
407 $row['contribution_status'] .= ' (' . ts('Pay Later') . ')';
408 $links[CRM_Core_Action
::ADD
] = [
409 'name' => ts('Pay with Credit Card'),
410 'url' => 'civicrm/contact/view/contribution',
411 'qs' => 'reset=1&action=update&id=%%id%%&cid=%%cid%%&context=%%cxt%%&mode=live',
412 'title' => ts('Pay with Credit Card'),
415 elseif (CRM_Utils_Array
::value('contribution_status_name', $row) == 'Pending') {
416 $row['contribution_status'] .= ' (' . ts('Incomplete Transaction') . ')';
419 if ($row['is_test']) {
420 $row['financial_type'] = $row['financial_type'] . ' (' . ts('test') . ')';
423 $row['checkbox'] = CRM_Core_Form
::CB_PREFIX
. $result->contribution_id
;
426 'id' => $result->contribution_id
,
427 'cid' => $result->contact_id
,
428 'cxt' => $this->_context
,
431 if (in_array($row['contribution_status_name'], ['Partially paid', 'Pending refund']) ||
$isPayLater) {
432 $buttonName = ts('Record Payment');
433 if ($row['contribution_status_name'] == 'Pending refund') {
434 $buttonName = ts('Record Refund');
436 elseif (CRM_Core_Config
::isEnabledBackOfficeCreditCardPayments()) {
437 $links[CRM_Core_Action
::BASIC
] = [
438 'name' => ts('Submit Credit Card payment'),
439 'url' => 'civicrm/payment/add',
440 'qs' => 'reset=1&id=%%id%%&cid=%%cid%%&action=add&component=contribution&mode=live',
441 'title' => ts('Submit Credit Card payment'),
444 $links[CRM_Core_Action
::ADD
] = [
445 'name' => $buttonName,
446 'url' => 'civicrm/payment',
447 'qs' => 'reset=1&id=%%id%%&cid=%%cid%%&action=add&component=contribution',
448 'title' => $buttonName,
451 $links = $links + CRM_Contribute_Task
::getContextualLinks($row);
453 $row['action'] = CRM_Core_Action
::formLink(
458 'contribution.selector.row',
460 $result->contribution_id
463 $row['contact_type'] = CRM_Contact_BAO_Contact_Utils
::getImage($result->contact_sub_type ?
$result->contact_sub_type
: $result->contact_type
, FALSE, $result->contact_id
466 if (!empty($row['amount_level'])) {
467 CRM_Event_BAO_Participant
::fixEventLevel($row['amount_level']);
479 public function getQILL() {
480 return $this->_query
->qill();
484 * Returns the column headers as an array of tuples:
485 * (name, sortName (key to the sort array))
487 * @param string $action
488 * The action being performed.
489 * @param string $output
490 * What should the result set include (web/email/csv).
493 * the column headers that need to be displayed
495 public function &getColumnHeaders($action = NULL, $output = NULL) {
497 self
::$_columnHeaders = [
499 'name' => $this->_includeSoftCredits ?
ts('Contribution Amount') : ts('Amount'),
500 'sort' => 'total_amount',
501 'direction' => CRM_Utils_Sort
::DONTCARE
,
502 'field_name' => 'total_amount',
506 if ($this->_includeSoftCredits
) {
507 self
::$_columnHeaders
509 self
::$_columnHeaders,
512 'name' => ts('Soft Credit Amount'),
513 'sort' => 'contribution_soft_credit_amount',
514 'field_name' => 'contribution_soft_credit_amount',
515 'direction' => CRM_Utils_Sort
::DONTCARE
,
521 self
::$_columnHeaders
523 self
::$_columnHeaders,
526 'name' => ts('Type'),
527 'sort' => 'financial_type',
528 'field_name' => 'financial_type',
529 'direction' => CRM_Utils_Sort
::DONTCARE
,
533 'name' => ts('Source'),
534 'sort' => 'contribution_source',
535 'field_name' => 'contribution_source',
536 'direction' => CRM_Utils_Sort
::DONTCARE
,
540 'name' => ts('Received'),
541 'sort' => 'receive_date',
542 'field_name' => 'receive_date',
544 'direction' => CRM_Utils_Sort
::DESCENDING
,
547 'name' => ts('Thank-you Sent'),
548 'sort' => 'thankyou_date',
549 'field_name' => 'thankyou_date',
551 'direction' => CRM_Utils_Sort
::DONTCARE
,
554 'name' => ts('Status'),
555 'sort' => 'contribution_status',
556 'field_name' => 'contribution_status',
557 'direction' => CRM_Utils_Sort
::DONTCARE
,
562 if (CRM_Contribute_BAO_Query
::isSiteHasProducts()) {
563 self
::$_columnHeaders[] = [
564 'name' => ts('Premium'),
565 'sort' => 'product_name',
566 'field_name' => 'product_name',
567 'direction' => CRM_Utils_Sort
::DONTCARE
,
571 if (!$this->_single
) {
574 'name' => ts('Name'),
575 'sort' => 'sort_name',
577 'direction' => CRM_Utils_Sort
::DONTCARE
,
582 self
::$_columnHeaders = array_merge($pre, self
::$_columnHeaders);
583 if ($this->_includeSoftCredits
) {
584 self
::$_columnHeaders = array_merge(
585 self
::$_columnHeaders,
588 'name' => ts('Soft Credit For'),
589 'sort' => 'contribution_soft_credit_name',
590 'direction' => CRM_Utils_Sort
::DONTCARE
,
594 'name' => ts('Soft Credit Type'),
595 'sort' => 'contribution_soft_credit_type',
596 'direction' => CRM_Utils_Sort
::ASCENDING
,
602 self
::$_columnHeaders
604 self
::$_columnHeaders, [
605 ['desc' => ts('Actions'), 'type' => 'actions', 'field_name' => ''],
608 foreach (array_keys(self
::$_columnHeaders) as $index) {
609 // Add weight & space it out a bit to allow headers to be inserted.
610 self
::$_columnHeaders[$index]['weight'] = $index * 10;
613 CRM_Core_Smarty
::singleton()->assign('softCreditColumns', $this->_includeSoftCredits
);
614 return self
::$_columnHeaders;
620 public function alphabetQuery() {
621 return $this->_query
->alphabetQuery();
625 * @return CRM_Contact_BAO_Query
627 public function &getQuery() {
628 return $this->_query
;
632 * Name of export file.
634 * @param string $output
640 public function getExportFileName($output = 'csv') {
641 return ts('CiviCRM Contribution Search');
647 public function getSummary() {
648 return $this->_query
->summaryContribution($this->_context
);