don't allow multiple currencies in a batch
[civicrm-core.git] / CRM / Batch / BAO / Batch.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
5 | |
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 +--------------------------------------------------------------------+
10 */
11
12 /**
13 *
14 * @package CRM
15 * @copyright CiviCRM LLC https://civicrm.org/licensing
16 */
17
18 /**
19 * Batch BAO class.
20 */
21 class CRM_Batch_BAO_Batch extends CRM_Batch_DAO_Batch {
22
23 /**
24 * Cache for the current batch object.
25 * @var object
26 */
27 public static $_batch = NULL;
28
29 /**
30 * Not sure this is the best way to do this. Depends on how exportFinancialBatch() below gets called.
31 * Maybe a parameter to that function is better.
32 * @var string
33 */
34 public static $_exportFormat = NULL;
35
36 /**
37 * Create a new batch.
38 *
39 * @param array $params
40 *
41 * @return object
42 * $batch batch object
43 * @throws \Exception
44 */
45 public static function create(&$params) {
46 if (empty($params['id']) && empty($params['name'])) {
47 $params['name'] = CRM_Utils_String::titleToVar($params['title'] ?? 'batch_ref_' . random_int(0, 100000));
48 }
49 return self::writeRecord($params);
50 }
51
52 /**
53 * Retrieve the information about the batch.
54 *
55 * @param array $params
56 * (reference ) an assoc array of name/value pairs.
57 * @param array $defaults
58 * (reference ) an assoc array to hold the flattened values.
59 *
60 * @return array
61 * CRM_Batch_BAO_Batch object on success, null otherwise
62 */
63 public static function retrieve(&$params, &$defaults) {
64 $batch = new CRM_Batch_DAO_Batch();
65 $batch->copyValues($params);
66 if ($batch->find(TRUE)) {
67 CRM_Core_DAO::storeValues($batch, $defaults);
68 return $batch;
69 }
70 return NULL;
71 }
72
73 /**
74 * Get profile id associated with the batch type.
75 *
76 * @param int $batchTypeId
77 * Batch type id.
78 *
79 * @return int
80 * $profileId profile id
81 */
82 public static function getProfileId($batchTypeId) {
83 //retrieve the profile specific to batch type
84 switch ($batchTypeId) {
85 case 1:
86 case 3:
87 //batch profile used for pledges
88 $profileName = "contribution_batch_entry";
89 break;
90
91 case 2:
92 //batch profile used for memberships
93 $profileName = "membership_batch_entry";
94 break;
95 }
96
97 // get and return the profile id
98 return CRM_Core_DAO::getFieldValue('CRM_Core_BAO_UFGroup', $profileName, 'id', 'name');
99 }
100
101 /**
102 * Generate batch name.
103 *
104 * @return string
105 * batch name
106 */
107 public static function generateBatchName() {
108 $sql = "SELECT max(id) FROM civicrm_batch";
109 $batchNo = CRM_Core_DAO::singleValueQuery($sql) + 1;
110 return ts('Batch %1', [1 => $batchNo]) . ': ' . date('Y-m-d');
111 }
112
113 /**
114 * Delete batch entry.
115 *
116 * @param int $batchId
117 * Batch id.
118 *
119 * @return bool
120 */
121 public static function deleteBatch($batchId) {
122 // delete entry from batch table
123 CRM_Utils_Hook::pre('delete', 'Batch', $batchId);
124 $batch = new CRM_Batch_DAO_Batch();
125 $batch->id = $batchId;
126 $batch->delete();
127 CRM_Utils_Hook::post('delete', 'Batch', $batch->id, $batch);
128 return TRUE;
129 }
130
131 /**
132 * wrapper for ajax batch selector.
133 *
134 * @param array $params
135 * Associated array for params record id.
136 *
137 * @return array
138 * associated array of batch list
139 */
140 public static function getBatchListSelector(&$params) {
141 // format the params
142 $params['offset'] = ($params['page'] - 1) * $params['rp'];
143 $params['rowCount'] = $params['rp'];
144 $params['sort'] = $params['sortBy'] ?? NULL;
145
146 // get batches
147 $batches = self::getBatchList($params);
148
149 // get batch totals for open batches
150 $fetchTotals = [];
151 $batchStatus = CRM_Core_PseudoConstant::get('CRM_Batch_DAO_Batch', 'status_id', ['labelColumn' => 'name']);
152 $batchStatus = [
153 array_search('Open', $batchStatus),
154 array_search('Reopened', $batchStatus),
155 ];
156 if ($params['context'] == 'financialBatch') {
157 foreach ($batches as $id => $batch) {
158 if (in_array($batch['status_id'], $batchStatus)) {
159 $fetchTotals[] = $id;
160 }
161 }
162 }
163 $totals = self::batchTotals($fetchTotals);
164
165 // add count
166 $params['total'] = self::getBatchCount($params);
167
168 // format params and add links
169 $batchList = [];
170
171 foreach ($batches as $id => $value) {
172 $batch = [];
173 if ($params['context'] == 'financialBatch') {
174 $batch['check'] = $value['check'];
175 }
176 $batch['batch_name'] = $value['title'];
177 $batch['total'] = '';
178 $batch['payment_instrument'] = $value['payment_instrument'];
179 $batch['item_count'] = $value['item_count'] ?? NULL;
180 $batch['type'] = $value['batch_type'] ?? NULL;
181 if (!empty($value['total'])) {
182 // CRM-21205
183 $batch['total'] = CRM_Utils_Money::format($value['total'], $value['currency']);
184 }
185
186 // Compare totals with actuals
187 if (isset($totals[$id])) {
188 $batch['item_count'] = self::displayTotals($totals[$id]['item_count'], $batch['item_count']);
189 $batch['total'] = self::displayTotals(CRM_Utils_Money::format($totals[$id]['total']), $batch['total']);
190 }
191 $batch['status'] = $value['batch_status'];
192 $batch['created_by'] = $value['created_by'];
193 $batch['links'] = $value['action'];
194 $batchList[$id] = $batch;
195 }
196 return $batchList;
197 }
198
199 /**
200 * Get list of batches.
201 *
202 * @param array $params
203 * Associated array for params.
204 *
205 * @return array
206 */
207 public static function getBatchList(&$params) {
208 $apiParams = self::whereClause($params);
209
210 if (!empty($params['rowCount']) && is_numeric($params['rowCount'])
211 && is_numeric($params['offset']) && $params['rowCount'] > 0
212 ) {
213 $apiParams['options'] = ['offset' => $params['offset'], 'limit' => $params['rowCount']];
214 }
215 $apiParams['options']['sort'] = 'id DESC';
216 if (!empty($params['sort'])) {
217 $apiParams['options']['sort'] = CRM_Utils_Type::escape($params['sort'], 'String');
218 }
219
220 $return = [
221 "id",
222 "name",
223 "title",
224 "description",
225 "created_date",
226 "status_id",
227 "modified_id",
228 "modified_date",
229 "type_id",
230 "mode_id",
231 "total",
232 "item_count",
233 "exported_date",
234 "payment_instrument_id",
235 "created_id.sort_name",
236 "created_id",
237 ];
238 $apiParams['return'] = $return;
239 $batches = civicrm_api3('Batch', 'get', $apiParams);
240 $obj = new CRM_Batch_BAO_Batch();
241 if (!empty($params['context'])) {
242 $links = $obj->links($params['context']);
243 }
244 else {
245 $links = $obj->links();
246 }
247
248 $batchTypes = CRM_Core_PseudoConstant::get('CRM_Batch_DAO_Batch', 'type_id');
249 $batchStatus = CRM_Core_PseudoConstant::get('CRM_Batch_DAO_Batch', 'status_id');
250 $batchStatusByName = CRM_Core_PseudoConstant::get('CRM_Batch_DAO_Batch', 'status_id', ['labelColumn' => 'name']);
251 $paymentInstrument = CRM_Contribute_PseudoConstant::paymentInstrument();
252
253 $results = [];
254 foreach ($batches['values'] as $values) {
255 $newLinks = $links;
256 $action = array_sum(array_keys($newLinks));
257
258 if ($values['status_id'] == array_search('Closed', $batchStatusByName) && $params['context'] != 'financialBatch') {
259 $newLinks = [];
260 }
261 elseif ($params['context'] == 'financialBatch') {
262 $values['check'] = "<input type='checkbox' id='check_" .
263 $values['id'] .
264 "' name='check_" .
265 $values['id'] .
266 "' value='1' data-status_id='" .
267 $values['status_id'] . "' class='select-row'></input>";
268
269 switch ($batchStatusByName[$values['status_id']]) {
270 case 'Open':
271 case 'Reopened':
272 CRM_Utils_Array::remove($newLinks, 'reopen', 'download');
273 break;
274
275 case 'Closed':
276 CRM_Utils_Array::remove($newLinks, 'close', 'edit', 'download');
277 break;
278
279 case 'Exported':
280 CRM_Utils_Array::remove($newLinks, 'close', 'edit', 'reopen', 'export');
281 }
282 if (!CRM_Batch_BAO_Batch::checkBatchPermission('edit', $values['created_id'])) {
283 CRM_Utils_Array::remove($newLinks, 'edit');
284 }
285 if (!CRM_Batch_BAO_Batch::checkBatchPermission('close', $values['created_id'])) {
286 CRM_Utils_Array::remove($newLinks, 'close', 'export');
287 }
288 if (!CRM_Batch_BAO_Batch::checkBatchPermission('reopen', $values['created_id'])) {
289 CRM_Utils_Array::remove($newLinks, 'reopen');
290 }
291 if (!CRM_Batch_BAO_Batch::checkBatchPermission('export', $values['created_id'])) {
292 CRM_Utils_Array::remove($newLinks, 'export', 'download');
293 }
294 if (!CRM_Batch_BAO_Batch::checkBatchPermission('delete', $values['created_id'])) {
295 CRM_Utils_Array::remove($newLinks, 'delete');
296 }
297 }
298 if (!empty($values['type_id'])) {
299 $values['batch_type'] = $batchTypes[$values['type_id']];
300 }
301 $values['batch_status'] = $batchStatus[$values['status_id']];
302 $values['created_by'] = $values['created_id.sort_name'];
303 $values['payment_instrument'] = '';
304 if (!empty($values['payment_instrument_id'])) {
305 $values['payment_instrument'] = $paymentInstrument[$values['payment_instrument_id']];
306 }
307 $tokens = ['id' => $values['id'], 'status' => $values['status_id']];
308 if ($values['status_id'] == array_search('Exported', $batchStatusByName)) {
309 $aid = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Export Accounting Batch');
310 $activityParams = ['source_record_id' => $values['id'], 'activity_type_id' => $aid];
311 $exportActivity = CRM_Activity_BAO_Activity::retrieve($activityParams, $val);
312 if ($exportActivity) {
313 $fid = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_EntityFile', $exportActivity->id, 'file_id', 'entity_id');
314 $fileHash = CRM_Core_BAO_File::generateFileHash($exportActivity->id, $fid);
315 $tokens = array_merge(['eid' => $exportActivity->id, 'fid' => $fid, 'fcs' => $fileHash], $tokens);
316 }
317 else {
318 CRM_Utils_Array::remove($newLinks, 'export', 'download');
319 }
320 }
321 $values['action'] = CRM_Core_Action::formLink(
322 $newLinks,
323 $action,
324 $tokens,
325 ts('more'),
326 FALSE,
327 'batch.selector.row',
328 'Batch',
329 $values['id']
330 );
331 // CRM-21205
332 $values['currency'] = CRM_Batch_BAO_EntityBatch::getBatchCurrency($values['id']);
333 $results[$values['id']] = $values;
334 }
335
336 return $results;
337 }
338
339 /**
340 * Get count of batches.
341 *
342 * @param array $params
343 * Associated array for params.
344 *
345 * @return null|string
346 */
347 public static function getBatchCount(&$params) {
348 $apiParams = self::whereClause($params);
349 return civicrm_api3('Batch', 'getCount', $apiParams);
350 }
351
352 /**
353 * Format where clause for getting lists of batches.
354 *
355 * @param array $params
356 * Associated array for params.
357 *
358 * @return string
359 */
360 public static function whereClause($params) {
361 $clauses = [];
362 // Exclude data-entry batches
363 $batchStatus = CRM_Core_PseudoConstant::get('CRM_Batch_DAO_Batch', 'status_id', ['labelColumn' => 'name']);
364 if (empty($params['status_id'])) {
365 $clauses['status_id'] = ['NOT IN' => ["Data Entry"]];
366 }
367
368 $return = [
369 "id",
370 "name",
371 "title",
372 "description",
373 "created_date",
374 "status_id",
375 "modified_id",
376 "modified_date",
377 "type_id",
378 "mode_id",
379 "total",
380 "item_count",
381 "exported_date",
382 "payment_instrument_id",
383 "created_id.sort_name",
384 "created_id",
385 ];
386 if (!CRM_Core_Permission::check("view all manual batches")) {
387 if (CRM_Core_Permission::check("view own manual batches")) {
388 $loggedInContactId = CRM_Core_Session::singleton()->get('userID');
389 $params['created_id'] = $loggedInContactId;
390 }
391 else {
392 $params['created_id'] = 0;
393 }
394 }
395 foreach ($return as $field) {
396 if (!isset($params[$field])) {
397 continue;
398 }
399 $value = CRM_Utils_Type::escape($params[$field], 'String', FALSE);
400 if (in_array($field, ['name', 'title', 'description', 'created_id.sort_name'])) {
401 $clauses[$field] = ['LIKE' => "%{$value}%"];
402 }
403 elseif ($field == 'status_id' && $value == array_search('Open', $batchStatus)) {
404 $clauses['status_id'] = ['IN' => ["Open", 'Reopened']];
405 }
406 else {
407 $clauses[$field] = $value;
408 }
409 }
410 return $clauses;
411 }
412
413 /**
414 * Define action links.
415 *
416 * @param null $context
417 *
418 * @return array
419 * array of action links
420 */
421 public function links($context = NULL) {
422 if ($context == 'financialBatch') {
423 $links = [
424 'transaction' => [
425 'name' => ts('Transactions'),
426 'url' => 'civicrm/batchtransaction',
427 'qs' => 'reset=1&bid=%%id%%',
428 'title' => ts('View/Add Transactions to Batch'),
429 ],
430 'edit' => [
431 'name' => ts('Edit'),
432 'url' => 'civicrm/financial/batch',
433 'qs' => 'reset=1&action=update&id=%%id%%&context=1',
434 'title' => ts('Edit Batch'),
435 ],
436 'close' => [
437 'name' => ts('Close'),
438 'title' => ts('Close Batch'),
439 'url' => '#',
440 'extra' => 'rel="close"',
441 ],
442 'export' => [
443 'name' => ts('Export'),
444 'title' => ts('Export Batch'),
445 'url' => 'civicrm/financial/batch/export',
446 'qs' => 'reset=1&id=%%id%%&status=1',
447 ],
448 'reopen' => [
449 'name' => ts('Re-open'),
450 'title' => ts('Re-open Batch'),
451 'url' => '#',
452 'extra' => 'rel="reopen"',
453 ],
454 'delete' => [
455 'name' => ts('Delete'),
456 'title' => ts('Delete Batch'),
457 'url' => '#',
458 'extra' => 'rel="delete"',
459 ],
460 'download' => [
461 'name' => ts('Download'),
462 'url' => 'civicrm/file',
463 'qs' => 'reset=1&id=%%fid%%&eid=%%eid%%&fcs=%%fcs%%',
464 'title' => ts('Download Batch'),
465 ],
466 ];
467 }
468 else {
469 $links = [
470 CRM_Core_Action::COPY => [
471 'name' => ts('Enter records'),
472 'url' => 'civicrm/batch/entry',
473 'qs' => 'id=%%id%%&reset=1',
474 'title' => ts('Batch Data Entry'),
475 ],
476 CRM_Core_Action::UPDATE => [
477 'name' => ts('Edit'),
478 'url' => 'civicrm/batch',
479 'qs' => 'action=update&id=%%id%%&reset=1',
480 'title' => ts('Edit Batch'),
481 ],
482 CRM_Core_Action::DELETE => [
483 'name' => ts('Delete'),
484 'url' => 'civicrm/batch',
485 'qs' => 'action=delete&id=%%id%%',
486 'title' => ts('Delete Batch'),
487 ],
488 ];
489 }
490 return $links;
491 }
492
493 /**
494 * Get batch list.
495 *
496 * @return array
497 * all batches excluding batches with data entry in progress
498 */
499 public static function getBatches() {
500 $dataEntryStatusId = CRM_Core_PseudoConstant::getKey('CRM_Batch_BAO_Batch', 'status_id', 'Data Entry');
501 $query = "SELECT id, title
502 FROM civicrm_batch
503 WHERE item_count >= 1
504 AND status_id != {$dataEntryStatusId}
505 ORDER BY title";
506
507 $batches = [];
508 $dao = CRM_Core_DAO::executeQuery($query);
509 while ($dao->fetch()) {
510 $batches[$dao->id] = $dao->title;
511 }
512 return $batches;
513 }
514
515 /**
516 * Calculate sum of all entries in a batch.
517 * Used to validate and update item_count and total when closing an accounting batch
518 *
519 * @param array $batchIds
520 * @return array
521 */
522 public static function batchTotals($batchIds) {
523 $totals = array_fill_keys($batchIds, ['item_count' => 0, 'total' => 0]);
524 if ($batchIds) {
525 $sql = "SELECT eb.batch_id, COUNT(tx.id) AS item_count, SUM(tx.total_amount) AS total
526 FROM civicrm_entity_batch eb
527 INNER JOIN civicrm_financial_trxn tx ON tx.id = eb.entity_id AND eb.entity_table = 'civicrm_financial_trxn'
528 WHERE eb.batch_id IN (" . implode(',', $batchIds) . ")
529 GROUP BY eb.batch_id";
530 $dao = CRM_Core_DAO::executeQuery($sql);
531 while ($dao->fetch()) {
532 $totals[$dao->batch_id] = (array) $dao;
533 }
534 }
535 return $totals;
536 }
537
538 /**
539 * Format markup for comparing two totals.
540 *
541 * @param $actual
542 * calculated total
543 * @param $expected
544 * user-entered total
545 * @return array
546 */
547 public static function displayTotals($actual, $expected) {
548 $class = 'actual-value';
549 if ($expected && $expected != $actual) {
550 $class .= ' crm-error';
551 }
552 $actualTitle = ts('Current Total');
553 $output = "<span class='$class' title='$actualTitle'>$actual</span>";
554 if ($expected) {
555 $expectedTitle = ts('Expected Total');
556 $output .= " / <span class='expected-value' title='$expectedTitle'>$expected</span>";
557 }
558 return $output;
559 }
560
561 /**
562 * Function for exporting financial accounts, currently we support CSV and IIF format
563 * @see http://wiki.civicrm.org/confluence/display/CRM/CiviAccounts+Specifications+-++Batches#CiviAccountsSpecifications-Batches-%C2%A0Overviewofimplementation
564 *
565 * @param array $batchIds
566 * Associated array of batch ids.
567 * @param string $exportFormat
568 * Export format.
569 * @param bool $downloadFile
570 * Download export file?.
571 */
572 public static function exportFinancialBatch($batchIds, $exportFormat, $downloadFile) {
573 if (empty($batchIds)) {
574 throw new CRM_Core_Exception(ts('No batches were selected.'));
575 }
576 if (empty($exportFormat)) {
577 throw new CRM_Core_Exception(ts('No export format selected.'));
578 }
579 self::$_exportFormat = $exportFormat;
580
581 // Instantiate appropriate exporter based on user-selected format.
582 $exporterClass = "CRM_Financial_BAO_ExportFormat_" . self::$_exportFormat;
583 if (class_exists($exporterClass)) {
584 $exporter = new $exporterClass();
585 }
586 else {
587 throw new CRM_Core_Exception("Could not locate exporter: $exporterClass");
588 }
589 $export = [];
590 $exporter->_isDownloadFile = $downloadFile;
591 foreach ($batchIds as $batchId) {
592 // export only batches whose status is set to Exported.
593 $result = civicrm_api3('Batch', 'getcount', [
594 'id' => $batchId,
595 'status_id' => "Exported",
596 ]);
597 if (!$result) {
598 continue;
599 }
600 $export[$batchId] = $exporter->generateExportQuery($batchId);
601 }
602 if ($export) {
603 $exporter->makeExport($export);
604 }
605 }
606
607 /**
608 * @param array $batchIds
609 * @param $status
610 */
611 public static function closeReOpen($batchIds, $status) {
612 $batchStatus = CRM_Core_PseudoConstant::get('CRM_Batch_DAO_Batch', 'status_id');
613 $params['status_id'] = CRM_Utils_Array::key($status, $batchStatus);
614 $session = CRM_Core_Session::singleton();
615 $params['modified_date'] = date('YmdHis');
616 $params['modified_id'] = $session->get('userID');
617 foreach ($batchIds as $key => $value) {
618 $params['id'] = $ids['batchID'] = $value;
619 self::create($params, $ids);
620 }
621 $url = CRM_Utils_System::url('civicrm/financial/financialbatches', "reset=1&batchStatus={$params['status_id']}");
622 CRM_Utils_System::redirect($url);
623 }
624
625 /**
626 * Retrieve financial items assigned for a batch.
627 *
628 * @param int $entityID
629 * @param array $returnValues
630 * @param bool $notPresent
631 * @param array $params
632 * @param bool $getCount
633 *
634 * @return CRM_Core_DAO
635 */
636 public static function getBatchFinancialItems($entityID, $returnValues, $notPresent = NULL, $params = NULL, $getCount = FALSE) {
637 if (!$getCount) {
638 if (!empty($params['rowCount']) &&
639 $params['rowCount'] > 0
640 ) {
641 $limit = " LIMIT {$params['offset']}, {$params['rowCount']} ";
642 }
643 }
644 // action is taken depending upon the mode
645 $select = 'civicrm_financial_trxn.id ';
646 if (!empty($returnValues)) {
647 $select .= " , " . implode(' , ', $returnValues);
648 }
649
650 $orderBy = " ORDER BY civicrm_financial_trxn.id";
651 if (!empty($params['sort'])) {
652 $orderBy = ' ORDER BY ' . CRM_Utils_Type::escape($params['sort'], 'String');
653 }
654
655 $from = "civicrm_financial_trxn
656 INNER JOIN civicrm_entity_financial_trxn ON civicrm_entity_financial_trxn.financial_trxn_id = civicrm_financial_trxn.id
657 INNER JOIN civicrm_contribution ON (civicrm_contribution.id = civicrm_entity_financial_trxn.entity_id
658 AND civicrm_entity_financial_trxn.entity_table='civicrm_contribution')
659 LEFT JOIN civicrm_entity_batch ON civicrm_entity_batch.entity_table = 'civicrm_financial_trxn'
660 AND civicrm_entity_batch.entity_id = civicrm_financial_trxn.id
661 LEFT JOIN civicrm_financial_type ON civicrm_financial_type.id = civicrm_contribution.financial_type_id
662 LEFT JOIN civicrm_contact contact_a ON contact_a.id = civicrm_contribution.contact_id
663 LEFT JOIN civicrm_contribution_soft ON civicrm_contribution_soft.contribution_id = civicrm_contribution.id
664 ";
665
666 $searchFields = [
667 'sort_name',
668 'financial_type_id',
669 'contribution_page_id',
670 'contribution_payment_instrument_id',
671 'contribution_trxn_id',
672 'contribution_source',
673 'contribution_currency_type',
674 'contribution_pay_later',
675 'contribution_recurring',
676 'contribution_test',
677 'contribution_thankyou_date_is_not_null',
678 'contribution_receipt_date_is_not_null',
679 'contribution_pcp_made_through_id',
680 'contribution_pcp_display_in_roll',
681 'contribution_amount_low',
682 'contribution_amount_high',
683 'contribution_in_honor_of',
684 'contact_tags',
685 'group',
686 'receive_date_relative',
687 'receive_date_high',
688 'receive_date_low',
689 'contribution_check_number',
690 'contribution_status_id',
691 'financial_trxn_card_type_id',
692 'financial_trxn_pan_truncation',
693 ];
694 $values = $customJoins = [];
695
696 // If a custom field was passed as a param,
697 // we'll take it into account.
698 $customSearchFields = [];
699 if (!empty($params)) {
700 foreach ($params as $name => $param) {
701 if (substr($name, 0, 6) == 'custom') {
702 $searchFields[] = $name;
703 }
704 }
705 }
706
707 foreach ($searchFields as $field) {
708 if (isset($params[$field])) {
709 $values[$field] = $params[$field];
710 if ($field == 'sort_name') {
711 $from .= " LEFT JOIN civicrm_contact contact_b ON contact_b.id = civicrm_contribution.contact_id
712 LEFT JOIN civicrm_email ON contact_b.id = civicrm_email.contact_id";
713 }
714 if ($field == 'contribution_in_honor_of') {
715 $from .= " LEFT JOIN civicrm_contact contact_b ON contact_b.id = civicrm_contribution.contact_id";
716 }
717 if ($field == 'contact_tags') {
718 $from .= " LEFT JOIN civicrm_entity_tag `civicrm_entity_tag-{$params[$field]}` ON `civicrm_entity_tag-{$params[$field]}`.entity_id = contact_a.id";
719 }
720 if ($field == 'group') {
721 $from .= " LEFT JOIN civicrm_group_contact `civicrm_group_contact-{$params[$field]}` ON contact_a.id = `civicrm_group_contact-{$params[$field]}`.contact_id ";
722 }
723 if ($field == 'receive_date_relative') {
724 $relativeDate = explode('.', $params[$field]);
725 $date = CRM_Utils_Date::relativeToAbsolute($relativeDate[0], $relativeDate[1]);
726 $values['receive_date_low'] = $date['from'];
727 $values['receive_date_high'] = $date['to'];
728 }
729
730 // Add left joins as they're needed to consider
731 // conditions over custom fields.
732 if (substr($field, 0, 6) == 'custom') {
733 $customFieldParams = ['id' => explode('_', $field)[1]];
734 $customFieldDefaults = [];
735 $customField = CRM_Core_BAO_CustomField::retrieve($customFieldParams, $customFieldDefaults);
736
737 $customGroupParams = ['id' => $customField->custom_group_id];
738 $customGroupDefaults = [];
739 $customGroup = CRM_Core_BAO_CustomGroup::retrieve($customGroupParams, $customGroupDefaults);
740
741 $columnName = $customField->column_name;
742 $tableName = $customGroup->table_name;
743
744 if (!array_key_exists($tableName, $customJoins)) {
745 $customJoins[$tableName] = "LEFT JOIN $tableName ON $tableName.entity_id = civicrm_contribution.id";
746 }
747 }
748 }
749 }
750
751 $searchParams = CRM_Contact_BAO_Query::convertFormValues(
752 $values,
753 0,
754 FALSE,
755 NULL,
756 [
757 'financial_type_id',
758 'contribution_soft_credit_type_id',
759 'contribution_status_id',
760 'contribution_page_id',
761 'financial_trxn_card_type_id',
762 'contribution_payment_instrument_id',
763 ]
764 );
765 // @todo the use of defaultReturnProperties means the search will be inefficient
766 // as slow-unneeded properties are included.
767 $query = new CRM_Contact_BAO_Query($searchParams,
768 CRM_Contribute_BAO_Query::defaultReturnProperties(CRM_Contact_BAO_Query::MODE_CONTRIBUTE,
769 FALSE
770 ), NULL, FALSE, FALSE, CRM_Contact_BAO_Query::MODE_CONTRIBUTE
771 );
772
773 if (count($customJoins) > 0) {
774 $from .= " " . implode(" ", $customJoins);
775 }
776
777 if (!empty($query->_where[0])) {
778 $where = implode(' AND ', $query->_where[0]) .
779 " AND civicrm_entity_batch.batch_id IS NULL ";
780 $where = str_replace('civicrm_contribution.payment_instrument_id', 'civicrm_financial_trxn.payment_instrument_id', $where);
781 }
782 else {
783 if (!$notPresent) {
784 $where = " civicrm_entity_batch.batch_id = {$entityID} ";
785 }
786 else {
787 $where = " civicrm_entity_batch.batch_id IS NULL ";
788 }
789 }
790
791 $sql = "
792 SELECT {$select}
793 FROM {$from}
794 WHERE {$where}
795 {$orderBy}
796 ";
797
798 if (isset($limit)) {
799 $sql .= "{$limit}";
800 }
801
802 $result = CRM_Core_DAO::executeQuery($sql);
803 return $result;
804 }
805
806 /**
807 * Get batch names.
808 * @param string $batchIds
809 *
810 * @return array
811 * array of batches
812 */
813 public static function getBatchNames($batchIds) {
814 $query = 'SELECT id, title
815 FROM civicrm_batch
816 WHERE id IN (' . $batchIds . ')';
817
818 $batches = [];
819 $dao = CRM_Core_DAO::executeQuery($query);
820 while ($dao->fetch()) {
821 $batches[$dao->id] = $dao->title;
822 }
823 return $batches;
824 }
825
826 /**
827 * Function get batch statuses.
828 *
829 * @param string $batchIds
830 *
831 * @return array
832 * array of batches
833 */
834 public static function getBatchStatuses($batchIds) {
835 $query = 'SELECT id, status_id
836 FROM civicrm_batch
837 WHERE id IN (' . $batchIds . ')';
838
839 $batches = [];
840 $dao = CRM_Core_DAO::executeQuery($query);
841 while ($dao->fetch()) {
842 $batches[$dao->id] = $dao->status_id;
843 }
844 return $batches;
845 }
846
847 /**
848 * Function to check permission for batch.
849 *
850 * @param string $action
851 * @param int $batchCreatedId
852 * batch created by contact id
853 *
854 * @return bool
855 */
856 public static function checkBatchPermission($action, $batchCreatedId = NULL) {
857 if (CRM_Core_Permission::check("{$action} all manual batches")) {
858 return TRUE;
859 }
860 if (CRM_Core_Permission::check("{$action} own manual batches")) {
861 $loggedInContactId = CRM_Core_Session::singleton()->get('userID');
862 if ($batchCreatedId == $loggedInContactId) {
863 return TRUE;
864 }
865 elseif (CRM_Utils_System::isNull($batchCreatedId)) {
866 return TRUE;
867 }
868 }
869 return FALSE;
870 }
871
872 }