Merge pull request #18131 from demeritcowboy/move-isssldsn
[civicrm-core.git] / tests / phpunit / CiviTest / CiviUnitTestCase.php
CommitLineData
6a488035
TO
1<?php
2/**
3 * File for the CiviUnitTestCase class
4 *
5 * (PHP 5)
6 *
7 * @copyright Copyright CiviCRM LLC (C) 2009
8 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html
9 * GNU Affero General Public License version 3
10 * @package CiviCRM
11 *
12 * This file is part of CiviCRM
13 *
14 * CiviCRM is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Affero General Public License
16 * as published by the Free Software Foundation; either version 3 of
17 * the License, or (at your option) any later version.
18 *
19 * CiviCRM is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Affero General Public License for more details.
23 *
24 * You should have received a copy of the GNU Affero General Public
25 * License along with this program. If not, see
26 * <http://www.gnu.org/licenses/>.
27 */
28
1fee3ad2 29use Civi\Payment\System;
68989e71 30use League\Csv\Reader;
1fee3ad2 31
6a488035
TO
32/**
33 * Include class definitions
34 */
6a488035 35require_once 'api/api.php';
6a488035
TO
36define('API_LATEST_VERSION', 3);
37
38/**
39 * Base class for CiviCRM unit tests
40 *
d67f1f28
TO
41 * This class supports two (mutually-exclusive) techniques for cleaning up test data. Subclasses
42 * may opt for one or neither:
43 *
44 * 1. quickCleanup() is a helper which truncates a series of tables. Call quickCleanup()
45 * as part of setUp() and/or tearDown(). quickCleanup() is thorough - but it can
46 * be cumbersome to use (b/c you must identify the tables to cleanup) and slow to execute.
47 * 2. useTransaction() executes the test inside a transaction. It's easier to use
48 * (because you don't need to identify specific tables), but it doesn't work for tests
49 * which manipulate schema or truncate data -- and could behave inconsistently
50 * for tests which specifically examine DB transactions.
51 *
6a488035 52 * Common functions for unit tests
7cf5bd55 53 *
6a488035
TO
54 * @package CiviCRM
55 */
1ee79258 56class CiviUnitTestCase extends PHPUnit\Framework\TestCase {
6a488035 57
195557d5 58 use \Civi\Test\Api3DocTrait;
1426d341 59 use \Civi\Test\GenericAssertionsTrait;
8c68b9c9 60 use \Civi\Test\DbTestTrait;
a23e13eb 61 use \Civi\Test\ContactTestTrait;
71f1227b 62 use \Civi\Test\MailingTestTrait;
30f5345c 63
6a488035 64 /**
eceb18cc 65 * Database has been initialized.
6a488035 66 *
d51c6add 67 * @var bool
6a488035
TO
68 */
69 private static $dbInit = FALSE;
70
71 /**
eceb18cc 72 * Database connection.
6a488035
TO
73 *
74 * @var PHPUnit_Extensions_Database_DB_IDatabaseConnection
75 */
76 protected $_dbconn;
77
78 /**
eceb18cc 79 * The database name.
6a488035
TO
80 *
81 * @var string
82 */
83 static protected $_dbName;
84
0b6f58fa 85 /**
eceb18cc 86 * Track tables we have modified during a test.
7cf5bd55 87 *
39b959db 88 * @var array
0b6f58fa 89 */
e255b57a 90 protected $_tablesToTruncate = [];
0b6f58fa 91
6a488035 92 /**
1d3260ea
SL
93 * @var array
94 * Array of temporary directory names
6a488035
TO
95 */
96 protected $tempDirs;
97
6a488035 98 /**
1d3260ea
SL
99 * @var bool
100 * populateOnce allows to skip db resets in setUp
6a488035
TO
101 *
102 * WARNING! USE WITH CAUTION - IT'LL RENDER DATA DEPENDENCIES
103 * BETWEEN TESTS WHEN RUN IN SUITE. SUITABLE FOR LOCAL, LIMITED
104 * "CHECK RUNS" ONLY!
105 *
106 * IF POSSIBLE, USE $this->DBResetRequired = FALSE IN YOUR TEST CASE!
107 *
1d3260ea 108 * @see http://forum.civicrm.org/index.php/topic,18065.0.html
6a488035
TO
109 */
110 public static $populateOnce = FALSE;
111
6a488035 112 /**
1d3260ea
SL
113 * DBResetRequired allows skipping DB reset
114 * in specific test case. If you still need
115 * to reset single test (method) of such case, call
116 * $this->cleanDB() in the first line of this
117 * test (method).
118 * @var bool
6a488035
TO
119 */
120 public $DBResetRequired = TRUE;
121
d67f1f28 122 /**
1d3260ea 123 * @var CRM_Core_Transaction|null
d67f1f28
TO
124 */
125 private $tx = NULL;
126
f9a5aadd 127 /**
128 * Array of IDs created to support the test.
129 *
130 * e.g
131 * $this->ids = ['Contact' => ['descriptive_key' => $contactID], 'Group' => [$groupID]];
132 *
133 * @var array
134 */
135 protected $ids = [];
136
6c466b51 137 /**
2af6f0c0 138 * Class used for hooks during tests.
139 *
140 * This can be used to test hooks within tests. For example in the ACL_PermissionTrait:
141 *
6c466b51 142 * $this->hookClass->setHook('civicrm_aclWhereClause', array($this, 'aclWhereHookAllResults'));
2af6f0c0 143 *
1d3260ea 144 * @var \CRM_Utils_Hook_UnitTests
6c466b51
EM
145 */
146 public $hookClass = NULL;
147
148 /**
1d3260ea
SL
149 * @var array
150 * Common values to be re-used multiple times within a class - usually to create the relevant entity
6c466b51 151 */
7cf5bd55 152 protected $_params = [];
6c466b51
EM
153
154 /**
155 * @var CRM_Extension_System
156 */
157 protected $origExtensionSystem;
158
204beda3 159 /**
160 * Array of IDs created during test setup routine.
161 *
162 * The cleanUpSetUpIds method can be used to clear these at the end of the test.
163 *
164 * @var array
165 */
7cf5bd55 166 public $setupIDs = [];
204beda3 167
6a488035 168 /**
eceb18cc 169 * Constructor.
6a488035
TO
170 *
171 * Because we are overriding the parent class constructor, we
172 * need to show the same arguments as exist in the constructor of
173 * PHPUnit_Framework_TestCase, since
174 * PHPUnit_Framework_TestSuite::createTest() creates a
175 * ReflectionClass of the Test class and checks the constructor
176 * of that class to decide how to set up the test.
177 *
e16033b4
TO
178 * @param string $name
179 * @param array $data
180 * @param string $dataName
6a488035 181 */
7cf5bd55 182 public function __construct($name = NULL, array $data = [], $dataName = '') {
6a488035
TO
183 parent::__construct($name, $data, $dataName);
184
185 // we need full error reporting
186 error_reporting(E_ALL & ~E_NOTICE);
187
f3e16511 188 self::$_dbName = self::getDBName();
6a488035 189
6a488035
TO
190 // also load the class loader
191 require_once 'CRM/Core/ClassLoader.php';
192 CRM_Core_ClassLoader::singleton()->register();
a130e045
DG
193 if (function_exists('_civix_phpunit_setUp')) {
194 // FIXME: loosen coupling
6a488035
TO
195 _civix_phpunit_setUp();
196 }
197 }
198
f0be539a
EM
199 /**
200 * Override to run the test and assert its state.
7cf5bd55 201 *
f0be539a
EM
202 * @return mixed
203 * @throws \Exception
204 * @throws \PHPUnit_Framework_IncompleteTest
205 * @throws \PHPUnit_Framework_SkippedTest
206 */
2e90bf37
TO
207 protected function runTest() {
208 try {
209 return parent::runTest();
5896d037
TO
210 }
211 catch (PEAR_Exception $e) {
2e90bf37
TO
212 // PEAR_Exception has metadata in funny places, and PHPUnit won't log it nicely
213 throw new Exception(\CRM_Core_Error::formatTextException($e), $e->getCode());
214 }
215 }
216
4cbe18b8
EM
217 /**
218 * @return bool
219 */
00be9182 220 public function requireDBReset() {
6a488035
TO
221 return $this->DBResetRequired;
222 }
223
4cbe18b8
EM
224 /**
225 * @return string
226 */
00be9182 227 public static function getDBName() {
f3e16511 228 static $dbName = NULL;
649d6dd0 229 if ($dbName === NULL) {
f3e16511
TO
230 require_once "DB.php";
231 $dsninfo = DB::parseDSN(CIVICRM_DSN);
232 $dbName = $dsninfo['database'];
233 }
6a488035
TO
234 return $dbName;
235 }
236
237 /**
eceb18cc 238 * Create database connection for this instance.
6a488035
TO
239 *
240 * Initialize the test database if it hasn't been initialized
241 *
6a488035
TO
242 */
243 protected function getConnection() {
6a488035
TO
244 if (!self::$dbInit) {
245 $dbName = self::getDBName();
246
247 // install test database
248 echo PHP_EOL . "Installing {$dbName} database" . PHP_EOL;
249
99117745 250 static::_populateDB(FALSE, $this);
6a488035
TO
251
252 self::$dbInit = TRUE;
253 }
bc560b0a 254
6a488035
TO
255 }
256
257 /**
eceb18cc 258 * Required implementation of abstract method.
6a488035
TO
259 */
260 protected function getDataSet() {
261 }
262
99117745
TO
263 /**
264 * @param bool $perClass
265 * @param null $object
7cf5bd55 266 *
a6c01b45
CW
267 * @return bool
268 * TRUE if the populate logic runs; FALSE if it is skipped
99117745
TO
269 */
270 protected static function _populateDB($perClass = FALSE, &$object = NULL) {
8582a9fc 271 if (CIVICRM_UF !== 'UnitTests') {
cdc44255 272 throw new \RuntimeException("_populateDB requires CIVICRM_UF=UnitTests");
8582a9fc 273 }
6a488035
TO
274
275 if ($perClass || $object == NULL) {
276 $dbreset = TRUE;
277 }
278 else {
279 $dbreset = $object->requireDBReset();
280 }
281
282 if (self::$populateOnce || !$dbreset) {
99117745 283 return FALSE;
6a488035
TO
284 }
285 self::$populateOnce = NULL;
286
728bbd5b 287 Civi\Test::data()->populate();
be709432
TO
288
289 return TRUE;
6a488035
TO
290 }
291
292 public static function setUpBeforeClass() {
99117745 293 static::_populateDB(TRUE);
6a488035
TO
294
295 // also set this global hack
7cf5bd55 296 $GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'] = [];
6a488035
TO
297 }
298
299 /**
eceb18cc 300 * Common setup functions for all unit tests.
6a488035
TO
301 */
302 protected function setUp() {
4db17da0
TO
303 $session = CRM_Core_Session::singleton();
304 $session->set('userID', NULL);
305
2d932085
CW
306 $this->_apiversion = 3;
307
39b959db
SL
308 // REVERT
309 $this->errorScope = CRM_Core_TemporaryErrorScope::useException();
6a488035
TO
310 // Use a temporary file for STDIN
311 $GLOBALS['stdin'] = tmpfile();
312 if ($GLOBALS['stdin'] === FALSE) {
313 echo "Couldn't open temporary file\n";
314 exit(1);
315 }
316
317 // Get and save a connection to the database
318 $this->_dbconn = $this->getConnection();
319
320 // reload database before each test
321 // $this->_populateDB();
322
323 // "initialize" CiviCRM to avoid problems when running single tests
324 // FIXME: look at it closer in second stage
325
c0a1f187
TO
326 $GLOBALS['civicrm_setting']['domain']['fatalErrorHandler'] = 'CiviUnitTestCase_fatalErrorHandler';
327 $GLOBALS['civicrm_setting']['domain']['backtrace'] = 1;
6a488035
TO
328
329 // disable any left-over test extensions
330 CRM_Core_DAO::executeQuery('DELETE FROM civicrm_extension WHERE full_name LIKE "test.%"');
331
332 // reset all the caches
333 CRM_Utils_System::flushCache();
334
c0a1f187 335 // initialize the object once db is loaded
7cf5bd55 336 \Civi::$statics = [];
39b959db
SL
337 // ugh, performance
338 $config = CRM_Core_Config::singleton(TRUE, TRUE);
c0a1f187
TO
339
340 // when running unit tests, use mockup user framework
edbcbd96 341 $this->hookClass = CRM_Utils_Hook::singleton();
c0a1f187 342
26684821
TO
343 // Make sure the DB connection is setup properly
344 $config->userSystem->setMySQLTimeZone();
a8de1c22 345 $env = new CRM_Utils_Check_Component_Env();
26684821
TO
346 CRM_Utils_Check::singleton()->assertValid($env->checkMysqlTime());
347
6a488035 348 // clear permissions stub to not check permissions
6a488035
TO
349 $config->userPermissionClass->permissions = NULL;
350
351 //flush component settings
352 CRM_Core_Component::getEnabledComponents(TRUE);
353
88a90442 354 $_REQUEST = $_GET = $_POST = [];
c3d574fc
EM
355 error_reporting(E_ALL);
356
91786f44 357 $this->renameLabels();
d58b453e 358 $this->_sethtmlGlobals();
6a488035
TO
359 }
360
0b6f58fa 361 /**
eceb18cc 362 * Read everything from the datasets directory and insert into the db.
0b6f58fa
ARW
363 */
364 public function loadAllFixtures() {
365 $fixturesDir = __DIR__ . '/../../fixtures';
366
8fee8f1c 367 CRM_Core_DAO::executeQuery("SET FOREIGN_KEY_CHECKS = 0;");
0b6f58fa 368
b01b546f
TO
369 $jsonFiles = glob($fixturesDir . '/*.json');
370 foreach ($jsonFiles as $jsonFixture) {
371 $json = json_decode(file_get_contents($jsonFixture));
372 foreach ($json as $tableName => $vars) {
8fee8f1c 373 if ($tableName === 'civicrm_contact') {
374 CRM_Core_DAO::executeQuery('DELETE c FROM civicrm_contact c LEFT JOIN civicrm_domain d ON d.contact_id = c.id WHERE d.id IS NULL');
375 }
376 else {
377 CRM_Core_DAO::executeQuery("TRUNCATE $tableName");
378 }
379 foreach ($vars as $entity) {
380 $keys = $values = [];
381 foreach ($entity as $key => $value) {
382 $keys[] = $key;
383 $values[] = is_numeric($value) ? $value : "'{$value}'";
384 }
385 CRM_Core_DAO::executeQuery("
386 INSERT INTO $tableName (" . implode(',', $keys) . ') VALUES(' . implode(',', $values) . ')'
387 );
388 }
389
390 }
0b6f58fa
ARW
391 }
392
8fee8f1c 393 CRM_Core_DAO::executeQuery("SET FOREIGN_KEY_CHECKS = 1;");
0b6f58fa
ARW
394 }
395
08d620bc 396 /**
397 * Load the data that used to be handled by the discontinued dbunit class.
398 *
399 * This could do with further tidy up - the initial priority is simply to get rid of
400 * the dbunity package which is no longer supported.
401 *
402 * @param string $fileName
403 */
404 protected function loadXMLDataSet($fileName) {
405 CRM_Core_DAO::executeQuery('SET FOREIGN_KEY_CHECKS = 0');
406 $xml = json_decode(json_encode(simplexml_load_file($fileName)), TRUE);
407 foreach ($xml as $tableName => $element) {
408 if (!empty($element)) {
409 foreach ($element as $row) {
410 $keys = $values = [];
411 if (isset($row['@attributes'])) {
412 foreach ($row['@attributes'] as $key => $value) {
413 $keys[] = $key;
414 $values[] = is_numeric($value) ? $value : "'{$value}'";
415 }
416 }
1d291c60 417 elseif (!empty($row)) {
08d620bc 418 // cos we copied it & it is inconsistent....
419 foreach ($row as $key => $value) {
420 $keys[] = $key;
421 $values[] = is_numeric($value) ? $value : "'{$value}'";
422 }
423 }
424
1d291c60 425 if (!empty($values)) {
426 CRM_Core_DAO::executeQuery("
08d620bc 427 INSERT INTO $tableName (" . implode(',', $keys) . ') VALUES(' . implode(',', $values) . ')'
1d291c60 428 );
429 }
08d620bc 430 }
431 }
432 }
433 CRM_Core_DAO::executeQuery('SET FOREIGN_KEY_CHECKS = 1');
434 }
435
5767aa07 436 /**
eceb18cc 437 * Create default domain contacts for the two domains added during test class.
5767aa07
AN
438 * database population.
439 */
440 public function createDomainContacts() {
1ca22999 441 $this->organizationCreate();
7cf5bd55 442 $this->organizationCreate(['organization_name' => 'Second Domain']);
5767aa07
AN
443 }
444
6a488035 445 /**
eceb18cc 446 * Common teardown functions for all unit tests.
6a488035
TO
447 */
448 protected function tearDown() {
2d932085 449 $this->_apiversion = 3;
91786f44 450 $this->resetLabels();
2d932085 451
6a488035 452 error_reporting(E_ALL & ~E_NOTICE);
6c466b51 453 CRM_Utils_Hook::singleton()->reset();
36a29dc0
TO
454 if ($this->hookClass) {
455 $this->hookClass->reset();
456 }
cbe41575 457 CRM_Core_Session::singleton()->reset(1);
d67f1f28
TO
458
459 if ($this->tx) {
460 $this->tx->rollback()->commit();
461 $this->tx = NULL;
462
463 CRM_Core_Transaction::forceRollbackIfEnabled();
464 \Civi\Core\Transaction\Manager::singleton(TRUE);
5896d037
TO
465 }
466 else {
d67f1f28
TO
467 CRM_Core_Transaction::forceRollbackIfEnabled();
468 \Civi\Core\Transaction\Manager::singleton(TRUE);
469
7cf5bd55 470 $tablesToTruncate = ['civicrm_contact', 'civicrm_uf_match'];
d67f1f28 471 $this->quickCleanup($tablesToTruncate);
a335f6b2 472 $this->createDomainContacts();
d67f1f28
TO
473 }
474
6a488035
TO
475 $this->cleanTempDirs();
476 $this->unsetExtensionSystem();
a80606eb 477 $this->assertEquals([], CRM_Core_DAO::$_nullArray);
478 $this->assertEquals(NULL, CRM_Core_DAO::$_nullObject);
6a488035
TO
479 }
480
888dab1e
TO
481 /**
482 * Create a batch of external API calls which can
483 * be executed concurrently.
484 *
0b882a86 485 * ```
888dab1e
TO
486 * $calls = $this->createExternalAPI()
487 * ->addCall('Contact', 'get', ...)
488 * ->addCall('Contact', 'get', ...)
489 * ...
490 * ->run()
491 * ->getResults();
0b882a86 492 * ```
888dab1e
TO
493 *
494 * @return \Civi\API\ExternalBatch
495 * @throws PHPUnit_Framework_SkippedTestError
496 */
497 public function createExternalAPI() {
498 global $civicrm_root;
7cf5bd55 499 $defaultParams = [
888dab1e
TO
500 'version' => $this->_apiversion,
501 'debug' => 1,
7cf5bd55 502 ];
888dab1e
TO
503
504 $calls = new \Civi\API\ExternalBatch($defaultParams);
888dab1e
TO
505
506 if (!$calls->isSupported()) {
507 $this->markTestSkipped('The test relies on Civi\API\ExternalBatch. This is unsupported in the local environment.');
508 }
509
510 return $calls;
511 }
512
fbda92d3 513 /**
514 * Create required data based on $this->entity & $this->params
515 * This is just a way to set up the test data for delete & get functions
516 * so the distinction between set
517 * up & tested functions is clearer
518 *
a6c01b45
CW
519 * @return array
520 * api Result
fbda92d3 521 */
5896d037 522 public function createTestEntity() {
fbda92d3 523 return $entity = $this->callAPISuccess($this->entity, 'create', $this->params);
524 }
525
4cbe18b8 526 /**
100fef9d 527 * @param int $contactTypeId
4cbe18b8
EM
528 *
529 * @throws Exception
530 */
00be9182 531 public function contactTypeDelete($contactTypeId) {
6a488035
TO
532 $result = CRM_Contact_BAO_ContactType::del($contactTypeId);
533 if (!$result) {
534 throw new Exception('Could not delete contact type');
535 }
536 }
537
4cbe18b8
EM
538 /**
539 * @param array $params
540 *
541 * @return mixed
542 */
7cf5bd55 543 public function membershipTypeCreate($params = []) {
6a488035
TO
544 CRM_Member_PseudoConstant::flush('membershipType');
545 CRM_Core_Config::clearDBCache();
204beda3 546 $this->setupIDs['contact'] = $memberOfOrganization = $this->organizationCreate();
7cf5bd55 547 $params = array_merge([
6a488035
TO
548 'name' => 'General',
549 'duration_unit' => 'year',
550 'duration_interval' => 1,
551 'period_type' => 'rolling',
0090e3d2 552 'member_of_contact_id' => $memberOfOrganization,
6a488035 553 'domain_id' => 1,
4303ea89 554 'financial_type_id' => 2,
6a488035 555 'is_active' => 1,
6a488035 556 'sequential' => 1,
eacf220c 557 'visibility' => 'Public',
7cf5bd55 558 ], $params);
75638074 559
560 $result = $this->callAPISuccess('MembershipType', 'Create', $params);
6a488035 561
6a488035
TO
562 CRM_Member_PseudoConstant::flush('membershipType');
563 CRM_Utils_Cache::singleton()->flush();
6a488035
TO
564
565 return $result['id'];
566 }
567
4cbe18b8 568 /**
c6ede1be 569 * Create membership.
570 *
c490a46a 571 * @param array $params
4cbe18b8 572 *
c6ede1be 573 * @return int
574 * @throws \CRM_Core_Exception
4cbe18b8 575 */
00be9182 576 public function contactMembershipCreate($params) {
7cf5bd55 577 $params = array_merge([
6a488035
TO
578 'join_date' => '2007-01-21',
579 'start_date' => '2007-01-21',
580 'end_date' => '2007-12-21',
581 'source' => 'Payment',
5d8b37be 582 'membership_type_id' => 'General',
7cf5bd55 583 ], $params);
5d8b37be 584 if (!is_numeric($params['membership_type_id'])) {
7cf5bd55 585 $membershipTypes = $this->callAPISuccess('Membership', 'getoptions', ['action' => 'create', 'field' => 'membership_type_id']);
c6ede1be 586 if (!in_array($params['membership_type_id'], $membershipTypes['values'], TRUE)) {
7cf5bd55 587 $this->membershipTypeCreate(['name' => $params['membership_type_id']]);
6a488035
TO
588 }
589 }
590
f6722559 591 $result = $this->callAPISuccess('Membership', 'create', $params);
6a488035
TO
592 return $result['id'];
593 }
594
595 /**
eceb18cc 596 * Delete Membership Type.
6a488035 597 *
c490a46a 598 * @param array $params
6a488035 599 */
00be9182 600 public function membershipTypeDelete($params) {
cab024d4 601 $this->callAPISuccess('MembershipType', 'Delete', $params);
6a488035
TO
602 }
603
4cbe18b8 604 /**
100fef9d 605 * @param int $membershipID
4cbe18b8 606 */
00be9182 607 public function membershipDelete($membershipID) {
7cf5bd55 608 $deleteParams = ['id' => $membershipID];
f6722559 609 $result = $this->callAPISuccess('Membership', 'Delete', $deleteParams);
6a488035
TO
610 }
611
4cbe18b8
EM
612 /**
613 * @param string $name
614 *
615 * @return mixed
31c3ac91 616 *
617 * @throws \CRM_Core_Exception
4cbe18b8 618 */
00be9182 619 public function membershipStatusCreate($name = 'test member status') {
6a488035
TO
620 $params['name'] = $name;
621 $params['start_event'] = 'start_date';
622 $params['end_event'] = 'end_date';
623 $params['is_current_member'] = 1;
624 $params['is_active'] = 1;
6a488035 625
f6722559 626 $result = $this->callAPISuccess('MembershipStatus', 'Create', $params);
6a488035 627 CRM_Member_PseudoConstant::flush('membershipStatus');
31c3ac91 628 return (int) $result['id'];
6a488035
TO
629 }
630
4cbe18b8 631 /**
31c3ac91 632 * Delete the given membership status, deleting any memberships of the status first.
633 *
100fef9d 634 * @param int $membershipStatusID
31c3ac91 635 *
636 * @throws \CRM_Core_Exception
4cbe18b8 637 */
31c3ac91 638 public function membershipStatusDelete(int $membershipStatusID) {
639 $this->callAPISuccess('Membership', 'get', ['status_id' => $membershipStatusID, 'api.Membership.delete' => 1]);
640 $this->callAPISuccess('MembershipStatus', 'Delete', ['id' => $membershipStatusID]);
6a488035
TO
641 }
642
17e615c7
MW
643 public function membershipRenewalDate($durationUnit, $membershipEndDate) {
644 // We only have an end_date if frequency units match, otherwise membership won't be autorenewed and dates won't be calculated.
645 $renewedMembershipEndDate = new DateTime($membershipEndDate);
646 switch ($durationUnit) {
647 case 'year':
648 $renewedMembershipEndDate->add(new DateInterval('P1Y'));
649 break;
650
651 case 'month':
652 // We have to add 1 day first in case it's the end of the month, then subtract afterwards
653 // eg. 2018-02-28 should renew to 2018-03-31, if we just added 1 month we'd get 2018-03-28
654 $renewedMembershipEndDate->add(new DateInterval('P1D'));
655 $renewedMembershipEndDate->add(new DateInterval('P1M'));
656 $renewedMembershipEndDate->sub(new DateInterval('P1D'));
657 break;
658 }
659 return $renewedMembershipEndDate->format('Y-m-d');
660 }
661
4cbe18b8 662 /**
b086dc3a 663 * Create a relationship type.
664 *
4cbe18b8
EM
665 * @param array $params
666 *
b086dc3a 667 * @return int
668 *
669 * @throws \CRM_Core_Exception
4cbe18b8 670 */
7cf5bd55 671 public function relationshipTypeCreate($params = []) {
672 $params = array_merge([
39b959db
SL
673 'name_a_b' => 'Relation 1 for relationship type create',
674 'name_b_a' => 'Relation 2 for relationship type create',
675 'contact_type_a' => 'Individual',
676 'contact_type_b' => 'Organization',
677 'is_reserved' => 1,
678 'is_active' => 1,
7cf5bd55 679 ], $params);
6a488035 680
f6722559 681 $result = $this->callAPISuccess('relationship_type', 'create', $params);
6a488035
TO
682 CRM_Core_PseudoConstant::flush('relationshipType');
683
684 return $result['id'];
685 }
686
687 /**
eceb18cc 688 * Delete Relatinship Type.
6a488035
TO
689 *
690 * @param int $relationshipTypeID
691 */
00be9182 692 public function relationshipTypeDelete($relationshipTypeID) {
6a488035 693 $params['id'] = $relationshipTypeID;
a60c0bc8
SL
694 $check = $this->callAPISuccess('relationship_type', 'get', $params);
695 if (!empty($check['count'])) {
696 $this->callAPISuccess('relationship_type', 'delete', $params);
697 }
6a488035
TO
698 }
699
4cbe18b8 700 /**
100fef9d 701 * @param array $params
4cbe18b8
EM
702 *
703 * @return mixed
b086dc3a 704 * @throws \CRM_Core_Exception
4cbe18b8 705 */
00be9182 706 public function paymentProcessorTypeCreate($params = NULL) {
6a488035 707 if (is_null($params)) {
7cf5bd55 708 $params = [
6a488035
TO
709 'name' => 'API_Test_PP',
710 'title' => 'API Test Payment Processor',
711 'class_name' => 'CRM_Core_Payment_APITest',
712 'billing_mode' => 'form',
713 'is_recur' => 0,
714 'is_reserved' => 1,
715 'is_active' => 1,
7cf5bd55 716 ];
6a488035 717 }
f6722559 718 $result = $this->callAPISuccess('payment_processor_type', 'create', $params);
6a488035 719
6a488035
TO
720 CRM_Core_PseudoConstant::flush('paymentProcessorType');
721
722 return $result['id'];
723 }
724
d6944518
EM
725 /**
726 * Create test Authorize.net instance.
727 *
728 * @param array $params
729 *
730 * @return mixed
731 */
7cf5bd55 732 public function paymentProcessorAuthorizeNetCreate($params = []) {
733 $params = array_merge([
d6944518
EM
734 'name' => 'Authorize',
735 'domain_id' => CRM_Core_Config::domainID(),
736 'payment_processor_type_id' => 'AuthNet',
737 'title' => 'AuthNet',
738 'is_active' => 1,
739 'is_default' => 0,
740 'is_test' => 1,
741 'is_recur' => 1,
742 'user_name' => '4y5BfuW7jm',
743 'password' => '4cAmW927n8uLf5J8',
744 'url_site' => 'https://test.authorize.net/gateway/transact.dll',
745 'url_recur' => 'https://apitest.authorize.net/xml/v1/request.api',
746 'class_name' => 'Payment_AuthorizeNet',
747 'billing_mode' => 1,
7cf5bd55 748 ], $params);
d6944518 749
4ecbf127 750 $result = $this->callAPISuccess('PaymentProcessor', 'create', $params);
d6944518
EM
751 return $result['id'];
752 }
753
6a488035 754 /**
eceb18cc 755 * Create Participant.
6a488035 756 *
e16033b4
TO
757 * @param array $params
758 * Array of contact id and event id values.
6a488035 759 *
a6c01b45
CW
760 * @return int
761 * $id of participant created
6a488035 762 */
0f57ba7a 763 public function participantCreate($params = []) {
5896d037 764 if (empty($params['contact_id'])) {
94692f2d 765 $params['contact_id'] = $this->individualCreate();
766 }
5896d037 767 if (empty($params['event_id'])) {
94692f2d 768 $event = $this->eventCreate();
769 $params['event_id'] = $event['id'];
770 }
7cf5bd55 771 $defaults = [
6a488035
TO
772 'status_id' => 2,
773 'role_id' => 1,
774 'register_date' => 20070219,
775 'source' => 'Wimbeldon',
776 'event_level' => 'Payment',
94692f2d 777 'debug' => 1,
7cf5bd55 778 ];
6a488035
TO
779
780 $params = array_merge($defaults, $params);
f6722559 781 $result = $this->callAPISuccess('Participant', 'create', $params);
6a488035
TO
782 return $result['id'];
783 }
784
785 /**
eceb18cc 786 * Create Payment Processor.
6a488035 787 *
8950ecdc 788 * @return int
789 * Id Payment Processor
6a488035 790 */
7cf5bd55 791 public function processorCreate($params = []) {
792 $processorParams = [
6a488035
TO
793 'domain_id' => 1,
794 'name' => 'Dummy',
8950ecdc 795 'payment_processor_type_id' => 'Dummy',
6a488035 796 'financial_account_id' => 12,
a8215a8d 797 'is_test' => TRUE,
6a488035
TO
798 'is_active' => 1,
799 'user_name' => '',
800 'url_site' => 'http://dummy.com',
801 'url_recur' => 'http://dummy.com',
802 'billing_mode' => 1,
8950ecdc 803 'sequential' => 1,
204efedd 804 'payment_instrument_id' => 'Debit Card',
7cf5bd55 805 ];
0f07bb06 806 $processorParams = array_merge($processorParams, $params);
8950ecdc 807 $processor = $this->callAPISuccess('PaymentProcessor', 'create', $processorParams);
808 return $processor['id'];
22e39333 809 }
810
811 /**
812 * Create Payment Processor.
813 *
814 * @param array $processorParams
815 *
0193ebdb 816 * @return \CRM_Core_Payment_Dummy
39b959db 817 * Instance of Dummy Payment Processor
31eddd0d 818 *
819 * @throws \CiviCRM_API3_Exception
22e39333 820 */
31eddd0d 821 public function dummyProcessorCreate($processorParams = []) {
8950ecdc 822 $paymentProcessorID = $this->processorCreate($processorParams);
31eddd0d 823 // For the tests we don't need a live processor, but as core ALWAYS creates a processor in live mode and one in test mode we do need to create both
824 // Otherwise we are testing a scenario that only exists in tests (and some tests fail because the live processor has not been defined).
825 $processorParams['is_test'] = FALSE;
826 $this->processorCreate($processorParams);
7a3b0ca3 827 return System::singleton()->getById($paymentProcessorID);
6a488035
TO
828 }
829
830 /**
eceb18cc 831 * Create contribution page.
6a488035 832 *
c490a46a 833 * @param array $params
7cf5bd55 834 *
16b10e64
CW
835 * @return array
836 * Array of contribution page
6a488035 837 */
7cf5bd55 838 public function contributionPageCreate($params = []) {
839 $this->_pageParams = array_merge([
6a488035
TO
840 'title' => 'Test Contribution Page',
841 'financial_type_id' => 1,
842 'currency' => 'USD',
843 'financial_account_id' => 1,
6a488035
TO
844 'is_active' => 1,
845 'is_allow_other_amount' => 1,
846 'min_amount' => 10,
847 'max_amount' => 1000,
7cf5bd55 848 ], $params);
23ead872 849 return $this->callAPISuccess('contribution_page', 'create', $this->_pageParams);
6a488035
TO
850 }
851
a646bdd2
SB
852 /**
853 * Create a sample batch.
854 */
855 public function batchCreate() {
856 $params = $this->_params;
857 $params['name'] = $params['title'] = 'Batch_433397';
858 $params['status_id'] = 1;
859 $result = $this->callAPISuccess('batch', 'create', $params);
860 return $result['id'];
861 }
862
6a488035 863 /**
eceb18cc 864 * Create Tag.
6a488035 865 *
2a6da8d7 866 * @param array $params
7cf5bd55 867 *
a6c01b45
CW
868 * @return array
869 * result of created tag
6a488035 870 */
7cf5bd55 871 public function tagCreate($params = []) {
872 $defaults = [
fb32de45 873 'name' => 'New Tag3',
874 'description' => 'This is description for Our New Tag ',
875 'domain_id' => '1',
7cf5bd55 876 ];
fb32de45 877 $params = array_merge($defaults, $params);
6c6e6187 878 $result = $this->callAPISuccess('Tag', 'create', $params);
fb32de45 879 return $result['values'][$result['id']];
6a488035
TO
880 }
881
882 /**
eceb18cc 883 * Delete Tag.
6a488035 884 *
e16033b4
TO
885 * @param int $tagId
886 * Id of the tag to be deleted.
16b10e64
CW
887 *
888 * @return int
6a488035 889 */
00be9182 890 public function tagDelete($tagId) {
6a488035 891 require_once 'api/api.php';
7cf5bd55 892 $params = [
6a488035 893 'tag_id' => $tagId,
7cf5bd55 894 ];
f6722559 895 $result = $this->callAPISuccess('Tag', 'delete', $params);
6a488035
TO
896 return $result['id'];
897 }
898
899 /**
900 * Add entity(s) to the tag
901 *
e16033b4 902 * @param array $params
77b97be7
EM
903 *
904 * @return bool
6a488035 905 */
00be9182 906 public function entityTagAdd($params) {
f6722559 907 $result = $this->callAPISuccess('entity_tag', 'create', $params);
6a488035
TO
908 return TRUE;
909 }
910
911 /**
5210fa35 912 * Create pledge.
6a488035 913 *
5210fa35 914 * @param array $params
915 * Parameters.
77b97be7 916 *
a6c01b45 917 * @return int
5210fa35 918 * id of created pledge
eca28463 919 *
920 * @throws \CRM_Core_Exception
6a488035 921 */
5210fa35 922 public function pledgeCreate($params) {
7cf5bd55 923 $params = array_merge([
6a488035
TO
924 'pledge_create_date' => date('Ymd'),
925 'start_date' => date('Ymd'),
926 'scheduled_date' => date('Ymd'),
927 'amount' => 100.00,
928 'pledge_status_id' => '2',
929 'financial_type_id' => '1',
930 'pledge_original_installment_amount' => 20,
931 'frequency_interval' => 5,
932 'frequency_unit' => 'year',
933 'frequency_day' => 15,
934 'installments' => 5,
7cf5bd55 935 ],
936 $params);
6a488035 937
f6722559 938 $result = $this->callAPISuccess('Pledge', 'create', $params);
6a488035
TO
939 return $result['id'];
940 }
941
942 /**
eceb18cc 943 * Delete contribution.
6a488035 944 *
c490a46a 945 * @param int $pledgeId
eca28463 946 *
947 * @throws \CRM_Core_Exception
6a488035 948 */
00be9182 949 public function pledgeDelete($pledgeId) {
7cf5bd55 950 $params = [
6a488035 951 'pledge_id' => $pledgeId,
7cf5bd55 952 ];
f6722559 953 $this->callAPISuccess('Pledge', 'delete', $params);
6a488035
TO
954 }
955
956 /**
eceb18cc 957 * Create contribution.
6a488035 958 *
c3a3074f 959 * @param array $params
960 * Array of parameters.
16b10e64 961 *
a6c01b45
CW
962 * @return int
963 * id of created contribution
7aeb7f06 964 * @throws \CRM_Core_Exception
6a488035 965 */
78ab0ca4 966 public function contributionCreate($params) {
69fccd8f 967
7cf5bd55 968 $params = array_merge([
6a488035 969 'domain_id' => 1,
6a488035
TO
970 'receive_date' => date('Ymd'),
971 'total_amount' => 100.00,
69fccd8f 972 'fee_amount' => 5.00,
78ab0ca4 973 'financial_type_id' => 1,
974 'payment_instrument_id' => 1,
6a488035 975 'non_deductible_amount' => 10.00,
6a488035 976 'source' => 'SSF',
6a488035 977 'contribution_status_id' => 1,
7cf5bd55 978 ], $params);
6a488035 979
f6722559 980 $result = $this->callAPISuccess('contribution', 'create', $params);
6a488035
TO
981 return $result['id'];
982 }
983
6a488035 984 /**
eceb18cc 985 * Delete contribution.
6a488035
TO
986 *
987 * @param int $contributionId
8deefe64
EM
988 *
989 * @return array|int
b086dc3a 990 * @throws \CRM_Core_Exception
6a488035 991 */
00be9182 992 public function contributionDelete($contributionId) {
7cf5bd55 993 $params = [
6a488035 994 'contribution_id' => $contributionId,
7cf5bd55 995 ];
f6722559 996 $result = $this->callAPISuccess('contribution', 'delete', $params);
6a488035
TO
997 return $result;
998 }
999
1000 /**
eceb18cc 1001 * Create an Event.
6a488035 1002 *
e16033b4
TO
1003 * @param array $params
1004 * Name-value pair for an event.
6a488035 1005 *
a6c01b45 1006 * @return array
beac0c97 1007 * @throws \CRM_Core_Exception
6a488035 1008 */
7cf5bd55 1009 public function eventCreate($params = []) {
6a488035
TO
1010 // if no contact was passed, make up a dummy event creator
1011 if (!isset($params['contact_id'])) {
7cf5bd55 1012 $params['contact_id'] = $this->_contactCreate([
6a488035
TO
1013 'contact_type' => 'Individual',
1014 'first_name' => 'Event',
1015 'last_name' => 'Creator',
7cf5bd55 1016 ]);
6a488035
TO
1017 }
1018
1019 // set defaults for missing params
7cf5bd55 1020 $params = array_merge([
6a488035
TO
1021 'title' => 'Annual CiviCRM meet',
1022 'summary' => 'If you have any CiviCRM related issues or want to track where CiviCRM is heading, Sign up now',
1023 'description' => 'This event is intended to give brief idea about progess of CiviCRM and giving solutions to common user issues',
1024 'event_type_id' => 1,
1025 'is_public' => 1,
1026 'start_date' => 20081021,
1027 'end_date' => 20081023,
1028 'is_online_registration' => 1,
1029 'registration_start_date' => 20080601,
1030 'registration_end_date' => 20081015,
1031 'max_participants' => 100,
1032 'event_full_text' => 'Sorry! We are already full',
c039f658 1033 'is_monetary' => 0,
6a488035 1034 'is_active' => 1,
6a488035 1035 'is_show_location' => 0,
e579af79 1036 'is_email_confirm' => 1,
7cf5bd55 1037 ], $params);
6a488035 1038
8cba5814 1039 return $this->callAPISuccess('Event', 'create', $params);
6a488035
TO
1040 }
1041
f660d2ad 1042 /**
1043 * Create a paid event.
1044 *
1045 * @param array $params
1046 *
2d6f64f4 1047 * @param array $options
1048 *
beac0c97 1049 * @param string $key
1050 * Index for storing event ID in ids array.
1051 *
f660d2ad 1052 * @return array
3fc37a30 1053 *
1054 * @throws \CRM_Core_Exception
f660d2ad 1055 */
beac0c97 1056 protected function eventCreatePaid($params, $options = [['name' => 'hundy', 'amount' => 100]], $key = 'event') {
57a7aa8a 1057 $params['is_monetary'] = TRUE;
f660d2ad 1058 $event = $this->eventCreate($params);
57a7aa8a 1059 $this->ids['Event'][$key] = (int) $event['id'];
1060 $this->ids['PriceSet'][$key] = $this->eventPriceSetCreate(55, 0, 'Radio', $options);
1061 CRM_Price_BAO_PriceSet::addTo('civicrm_event', $event['id'], $this->ids['PriceSet'][$key]);
1062 $priceSet = CRM_Price_BAO_PriceSet::getSetDetail($this->ids['PriceSet'][$key], TRUE, FALSE);
1063 $priceSet = $priceSet[$this->ids['PriceSet'][$key]] ?? NULL;
849ac823 1064 $this->eventFeeBlock = $priceSet['fields'] ?? NULL;
f660d2ad 1065 return $event;
1066 }
1067
6a488035 1068 /**
eceb18cc 1069 * Delete event.
6a488035 1070 *
e16033b4
TO
1071 * @param int $id
1072 * ID of the event.
8deefe64
EM
1073 *
1074 * @return array|int
6a488035 1075 */
00be9182 1076 public function eventDelete($id) {
7cf5bd55 1077 $params = [
6a488035 1078 'event_id' => $id,
7cf5bd55 1079 ];
8cba5814 1080 return $this->callAPISuccess('event', 'delete', $params);
6a488035
TO
1081 }
1082
1083 /**
eceb18cc 1084 * Delete participant.
6a488035
TO
1085 *
1086 * @param int $participantID
8deefe64
EM
1087 *
1088 * @return array|int
6a488035 1089 */
00be9182 1090 public function participantDelete($participantID) {
7cf5bd55 1091 $params = [
6a488035 1092 'id' => $participantID,
7cf5bd55 1093 ];
a60c0bc8
SL
1094 $check = $this->callAPISuccess('Participant', 'get', $params);
1095 if ($check['count'] > 0) {
1096 return $this->callAPISuccess('Participant', 'delete', $params);
1097 }
6a488035
TO
1098 }
1099
1100 /**
eceb18cc 1101 * Create participant payment.
6a488035 1102 *
100fef9d
CW
1103 * @param int $participantID
1104 * @param int $contributionID
7cf5bd55 1105 *
a6c01b45
CW
1106 * @return int
1107 * $id of created payment
6a488035 1108 */
00be9182 1109 public function participantPaymentCreate($participantID, $contributionID = NULL) {
6a488035 1110 //Create Participant Payment record With Values
7cf5bd55 1111 $params = [
6a488035
TO
1112 'participant_id' => $participantID,
1113 'contribution_id' => $contributionID,
7cf5bd55 1114 ];
6a488035 1115
f6722559 1116 $result = $this->callAPISuccess('participant_payment', 'create', $params);
6a488035
TO
1117 return $result['id'];
1118 }
1119
1120 /**
eceb18cc 1121 * Delete participant payment.
6a488035
TO
1122 *
1123 * @param int $paymentID
1124 */
00be9182 1125 public function participantPaymentDelete($paymentID) {
7cf5bd55 1126 $params = [
6a488035 1127 'id' => $paymentID,
7cf5bd55 1128 ];
f6722559 1129 $result = $this->callAPISuccess('participant_payment', 'delete', $params);
6a488035
TO
1130 }
1131
1132 /**
eceb18cc 1133 * Add a Location.
6a488035 1134 *
100fef9d 1135 * @param int $contactID
7cf5bd55 1136 *
a6c01b45
CW
1137 * @return int
1138 * location id of created location
6a488035 1139 */
00be9182 1140 public function locationAdd($contactID) {
7cf5bd55 1141 $address = [
1142 1 => [
6a488035
TO
1143 'location_type' => 'New Location Type',
1144 'is_primary' => 1,
1145 'name' => 'Saint Helier St',
1146 'county' => 'Marin',
86797006 1147 'country' => 'UNITED STATES',
6a488035
TO
1148 'state_province' => 'Michigan',
1149 'supplemental_address_1' => 'Hallmark Ct',
1150 'supplemental_address_2' => 'Jersey Village',
207f62c6 1151 'supplemental_address_3' => 'My Town',
7cf5bd55 1152 ],
1153 ];
6a488035 1154
7cf5bd55 1155 $params = [
6a488035
TO
1156 'contact_id' => $contactID,
1157 'address' => $address,
6a488035
TO
1158 'location_format' => '2.0',
1159 'location_type' => 'New Location Type',
7cf5bd55 1160 ];
6a488035 1161
f6722559 1162 $result = $this->callAPISuccess('Location', 'create', $params);
6a488035
TO
1163 return $result;
1164 }
1165
1166 /**
eceb18cc 1167 * Delete Locations of contact.
6a488035 1168 *
e16033b4
TO
1169 * @param array $params
1170 * Parameters.
6a488035 1171 */
00be9182 1172 public function locationDelete($params) {
c490a46a 1173 $this->callAPISuccess('Location', 'delete', $params);
6a488035
TO
1174 }
1175
1176 /**
eceb18cc 1177 * Add a Location Type.
6a488035 1178 *
100fef9d 1179 * @param array $params
7cf5bd55 1180 *
16b10e64
CW
1181 * @return CRM_Core_DAO_LocationType
1182 * location id of created location
6a488035 1183 */
00be9182 1184 public function locationTypeCreate($params = NULL) {
6a488035 1185 if ($params === NULL) {
7cf5bd55 1186 $params = [
6a488035
TO
1187 'name' => 'New Location Type',
1188 'vcard_name' => 'New Location Type',
1189 'description' => 'Location Type for Delete',
1190 'is_active' => 1,
7cf5bd55 1191 ];
6a488035
TO
1192 }
1193
6a488035
TO
1194 $locationType = new CRM_Core_DAO_LocationType();
1195 $locationType->copyValues($params);
1196 $locationType->save();
1197 // clear getfields cache
2683ce94 1198 CRM_Core_PseudoConstant::flush();
7cf5bd55 1199 $this->callAPISuccess('phone', 'getfields', ['version' => 3, 'cache_clear' => 1]);
6a488035
TO
1200 return $locationType;
1201 }
1202
1203 /**
eceb18cc 1204 * Delete a Location Type.
6a488035 1205 *
79d7553f 1206 * @param int $locationTypeId
6a488035 1207 */
00be9182 1208 public function locationTypeDelete($locationTypeId) {
6a488035
TO
1209 $locationType = new CRM_Core_DAO_LocationType();
1210 $locationType->id = $locationTypeId;
1211 $locationType->delete();
1212 }
1213
927f93e2
TM
1214 /**
1215 * Add a Mapping.
1216 *
1217 * @param array $params
7cf5bd55 1218 *
927f93e2
TM
1219 * @return CRM_Core_DAO_Mapping
1220 * Mapping id of created mapping
1221 */
1222 public function mappingCreate($params = NULL) {
1223 if ($params === NULL) {
7cf5bd55 1224 $params = [
927f93e2
TM
1225 'name' => 'Mapping name',
1226 'description' => 'Mapping description',
1227 // 'Export Contact' mapping.
1228 'mapping_type_id' => 7,
7cf5bd55 1229 ];
927f93e2
TM
1230 }
1231
1232 $mapping = new CRM_Core_DAO_Mapping();
1233 $mapping->copyValues($params);
1234 $mapping->save();
1235 // clear getfields cache
1236 CRM_Core_PseudoConstant::flush();
7cf5bd55 1237 $this->callAPISuccess('mapping', 'getfields', ['version' => 3, 'cache_clear' => 1]);
927f93e2
TM
1238 return $mapping;
1239 }
1240
1241 /**
1242 * Delete a Mapping
1243 *
1244 * @param int $mappingId
1245 */
1246 public function mappingDelete($mappingId) {
1247 $mapping = new CRM_Core_DAO_Mapping();
1248 $mapping->id = $mappingId;
1249 $mapping->delete();
1250 }
1251
5e8daa54 1252 /**
1253 * Prepare class for ACLs.
1254 */
1255 protected function prepareForACLs() {
1256 $config = CRM_Core_Config::singleton();
7cf5bd55 1257 $config->userPermissionClass->permissions = [];
5e8daa54 1258 }
1259
1260 /**
1261 * Reset after ACLs.
1262 */
1263 protected function cleanUpAfterACLs() {
1264 CRM_Utils_Hook::singleton()->reset();
7cf5bd55 1265 $tablesToTruncate = [
5e8daa54 1266 'civicrm_acl',
1267 'civicrm_acl_cache',
1268 'civicrm_acl_entity_role',
1269 'civicrm_acl_contact_cache',
7cf5bd55 1270 ];
5e8daa54 1271 $this->quickCleanup($tablesToTruncate);
1272 $config = CRM_Core_Config::singleton();
1273 unset($config->userPermissionClass->permissions);
1274 }
39b959db 1275
c3137c08 1276 /**
1277 * Create a smart group.
1278 *
1279 * By default it will be a group of households.
1280 *
1281 * @param array $smartGroupParams
1282 * @param array $groupParams
0d38cb6d 1283 * @param string $contactType
1284 *
c3137c08 1285 * @return int
1286 */
0d38cb6d 1287 public function smartGroupCreate($smartGroupParams = [], $groupParams = [], $contactType = 'Household') {
fe38e286 1288 $smartGroupParams = array_merge(['form_values' => ['contact_type' => ['IN' => [$contactType]]]], $smartGroupParams);
c3137c08 1289 $savedSearch = CRM_Contact_BAO_SavedSearch::create($smartGroupParams);
1290
1291 $groupParams['saved_search_id'] = $savedSearch->id;
1292 return $this->groupCreate($groupParams);
1293 }
7811a84b 1294
5c496c74 1295 /**
eceb18cc 1296 * Create a UFField.
7cf5bd55 1297 *
5c496c74 1298 * @param array $params
1299 */
7cf5bd55 1300 public function uFFieldCreate($params = []) {
1301 $params = array_merge([
5c496c74 1302 'uf_group_id' => 1,
1303 'field_name' => 'first_name',
1304 'is_active' => 1,
1305 'is_required' => 1,
1306 'visibility' => 'Public Pages and Listings',
1307 'is_searchable' => '1',
1308 'label' => 'first_name',
1309 'field_type' => 'Individual',
1310 'weight' => 1,
7cf5bd55 1311 ], $params);
5c496c74 1312 $this->callAPISuccess('uf_field', 'create', $params);
1313 }
2a6da8d7 1314
6a488035 1315 /**
eceb18cc 1316 * Add a UF Join Entry.
6a488035 1317 *
100fef9d 1318 * @param array $params
7cf5bd55 1319 *
a6c01b45
CW
1320 * @return int
1321 * $id of created UF Join
6a488035 1322 */
00be9182 1323 public function ufjoinCreate($params = NULL) {
6a488035 1324 if ($params === NULL) {
7cf5bd55 1325 $params = [
6a488035
TO
1326 'is_active' => 1,
1327 'module' => 'CiviEvent',
1328 'entity_table' => 'civicrm_event',
1329 'entity_id' => 3,
1330 'weight' => 1,
1331 'uf_group_id' => 1,
7cf5bd55 1332 ];
6a488035 1333 }
f6722559 1334 $result = $this->callAPISuccess('uf_join', 'create', $params);
6a488035
TO
1335 return $result;
1336 }
1337
c03f1689
EM
1338 /**
1339 * @param array $params
1340 * Optional parameters.
d31d0888 1341 * @param bool $reloadConfig
1342 * While enabling CiviCampaign component, we shouldn't always forcibly
1343 * reload config as this hinder hook call in test environment
c03f1689
EM
1344 *
1345 * @return int
1346 * Campaign ID.
1347 */
7cf5bd55 1348 public function campaignCreate($params = [], $reloadConfig = TRUE) {
d31d0888 1349 $this->enableCiviCampaign($reloadConfig);
7cf5bd55 1350 $campaign = $this->callAPISuccess('campaign', 'create', array_merge([
c03f1689
EM
1351 'name' => 'big_campaign',
1352 'title' => 'Campaign',
7cf5bd55 1353 ], $params));
c03f1689
EM
1354 return $campaign['id'];
1355 }
1356
6a488035 1357 /**
eceb18cc 1358 * Create Group for a contact.
6a488035
TO
1359 *
1360 * @param int $contactId
1361 */
00be9182 1362 public function contactGroupCreate($contactId) {
7cf5bd55 1363 $params = [
6a488035
TO
1364 'contact_id.1' => $contactId,
1365 'group_id' => 1,
7cf5bd55 1366 ];
6a488035 1367
f6722559 1368 $this->callAPISuccess('GroupContact', 'Create', $params);
6a488035
TO
1369 }
1370
1371 /**
eceb18cc 1372 * Delete Group for a contact.
6a488035 1373 *
100fef9d 1374 * @param int $contactId
6a488035 1375 */
00be9182 1376 public function contactGroupDelete($contactId) {
7cf5bd55 1377 $params = [
6a488035
TO
1378 'contact_id.1' => $contactId,
1379 'group_id' => 1,
7cf5bd55 1380 ];
0f643fb2 1381 $this->civicrm_api('GroupContact', 'Delete', $params);
6a488035
TO
1382 }
1383
1384 /**
eceb18cc 1385 * Create Activity.
6a488035 1386 *
c490a46a 1387 * @param array $params
7cf5bd55 1388 *
2a6da8d7 1389 * @return array|int
b650fe2c 1390 *
1391 * @throws \CRM_Core_Exception
1392 * @throws \CiviCRM_API3_Exception
6a488035 1393 */
7cf5bd55 1394 public function activityCreate($params = []) {
1395 $params = array_merge([
5d8b37be 1396 'subject' => 'Discussion on warm beer',
1397 'activity_date_time' => date('Ymd'),
da9977bd 1398 'duration' => 90,
5d8b37be 1399 'location' => 'Baker Street',
1400 'details' => 'Lets schedule a meeting',
1401 'status_id' => 1,
3af8de9f 1402 'activity_type_id' => 'Meeting',
7cf5bd55 1403 ], $params);
5d8b37be 1404 if (!isset($params['source_contact_id'])) {
1405 $params['source_contact_id'] = $this->individualCreate();
1406 }
1407 if (!isset($params['target_contact_id'])) {
7cf5bd55 1408 $params['target_contact_id'] = $this->individualCreate([
6a488035 1409 'first_name' => 'Julia',
573aab99 1410 'last_name' => 'Anderson',
6a488035
TO
1411 'prefix' => 'Ms.',
1412 'email' => 'julia_anderson@civicrm.org',
1413 'contact_type' => 'Individual',
7cf5bd55 1414 ]);
5d8b37be 1415 }
1416 if (!isset($params['assignee_contact_id'])) {
1417 $params['assignee_contact_id'] = $params['target_contact_id'];
6a488035
TO
1418 }
1419
2d932085 1420 $result = civicrm_api3('Activity', 'create', $params);
6a488035 1421
5d8b37be 1422 $result['target_contact_id'] = $params['target_contact_id'];
1423 $result['assignee_contact_id'] = $params['assignee_contact_id'];
6a488035
TO
1424 return $result;
1425 }
1426
1427 /**
eceb18cc 1428 * Create an activity type.
6a488035 1429 *
e16033b4
TO
1430 * @param array $params
1431 * Parameters.
7cf5bd55 1432 *
f0be539a 1433 * @return array
6a488035 1434 */
00be9182 1435 public function activityTypeCreate($params) {
f0be539a 1436 return $this->callAPISuccess('ActivityType', 'create', $params);
79d7553f 1437 }
6a488035
TO
1438
1439 /**
eceb18cc 1440 * Delete activity type.
6a488035 1441 *
e16033b4
TO
1442 * @param int $activityTypeId
1443 * Id of the activity type.
7cf5bd55 1444 *
f0be539a 1445 * @return array
6a488035 1446 */
00be9182 1447 public function activityTypeDelete($activityTypeId) {
6a488035 1448 $params['activity_type_id'] = $activityTypeId;
f0be539a 1449 return $this->callAPISuccess('ActivityType', 'delete', $params);
79d7553f 1450 }
6a488035
TO
1451
1452 /**
eceb18cc 1453 * Create custom group.
6a488035 1454 *
2a6da8d7 1455 * @param array $params
7cf5bd55 1456 *
27dd6252 1457 * @return array
6a488035 1458 */
7cf5bd55 1459 public function customGroupCreate($params = []) {
1460 $defaults = [
f6722559 1461 'title' => 'new custom group',
1462 'extends' => 'Contact',
1463 'domain_id' => 1,
1464 'style' => 'Inline',
1465 'is_active' => 1,
7cf5bd55 1466 ];
6a488035 1467
f6722559 1468 $params = array_merge($defaults, $params);
1469
f6722559 1470 return $this->callAPISuccess('custom_group', 'create', $params);
6a488035
TO
1471 }
1472
1473 /**
100fef9d 1474 * Existing function doesn't allow params to be over-ridden so need a new one
6a488035 1475 * this one allows you to only pass in the params you want to change
7cf5bd55 1476 *
1e1fdcf6 1477 * @param array $params
7cf5bd55 1478 *
1e1fdcf6 1479 * @return array|int
6a488035 1480 */
7cf5bd55 1481 public function CustomGroupCreateByParams($params = []) {
1482 $defaults = [
6a488035
TO
1483 'title' => "API Custom Group",
1484 'extends' => 'Contact',
1485 'domain_id' => 1,
1486 'style' => 'Inline',
1487 'is_active' => 1,
7cf5bd55 1488 ];
6a488035 1489 $params = array_merge($defaults, $params);
f6722559 1490 return $this->callAPISuccess('custom_group', 'create', $params);
6a488035
TO
1491 }
1492
1493 /**
eceb18cc 1494 * Create custom group with multi fields.
7cf5bd55 1495 *
1e1fdcf6 1496 * @param array $params
7cf5bd55 1497 *
1e1fdcf6 1498 * @return array|int
6a488035 1499 */
7cf5bd55 1500 public function CustomGroupMultipleCreateByParams($params = []) {
1501 $defaults = [
6a488035
TO
1502 'style' => 'Tab',
1503 'is_multiple' => 1,
7cf5bd55 1504 ];
6a488035 1505 $params = array_merge($defaults, $params);
f6722559 1506 return $this->CustomGroupCreateByParams($params);
6a488035
TO
1507 }
1508
1509 /**
eceb18cc 1510 * Create custom group with multi fields.
7cf5bd55 1511 *
1e1fdcf6 1512 * @param array $params
7cf5bd55 1513 *
1e1fdcf6 1514 * @return array
6a488035 1515 */
7cf5bd55 1516 public function CustomGroupMultipleCreateWithFields($params = []) {
6a488035 1517 // also need to pass on $params['custom_field'] if not set but not in place yet
7cf5bd55 1518 $ids = [];
6a488035
TO
1519 $customGroup = $this->CustomGroupMultipleCreateByParams($params);
1520 $ids['custom_group_id'] = $customGroup['id'];
6a488035 1521
7cf5bd55 1522 $customField = $this->customFieldCreate([
92915c55
TO
1523 'custom_group_id' => $ids['custom_group_id'],
1524 'label' => 'field_1' . $ids['custom_group_id'],
e525d6af 1525 'in_selector' => 1,
7cf5bd55 1526 ]);
6a488035
TO
1527
1528 $ids['custom_field_id'][] = $customField['id'];
f6722559 1529
7cf5bd55 1530 $customField = $this->customFieldCreate([
92915c55
TO
1531 'custom_group_id' => $ids['custom_group_id'],
1532 'default_value' => '',
1533 'label' => 'field_2' . $ids['custom_group_id'],
e525d6af 1534 'in_selector' => 1,
7cf5bd55 1535 ]);
6a488035 1536 $ids['custom_field_id'][] = $customField['id'];
f6722559 1537
7cf5bd55 1538 $customField = $this->customFieldCreate([
92915c55
TO
1539 'custom_group_id' => $ids['custom_group_id'],
1540 'default_value' => '',
1541 'label' => 'field_3' . $ids['custom_group_id'],
e525d6af 1542 'in_selector' => 1,
7cf5bd55 1543 ]);
6a488035 1544 $ids['custom_field_id'][] = $customField['id'];
f6722559 1545
6a488035
TO
1546 return $ids;
1547 }
1548
1549 /**
1550 * Create a custom group with a single text custom field. See
1551 * participant:testCreateWithCustom for how to use this
1552 *
e16033b4
TO
1553 * @param string $function
1554 * __FUNCTION__.
5a4f6742
CW
1555 * @param string $filename
1556 * $file __FILE__.
6a488035 1557 *
a6c01b45
CW
1558 * @return array
1559 * ids of created objects
6a488035 1560 */
00be9182 1561 public function entityCustomGroupWithSingleFieldCreate($function, $filename) {
7cf5bd55 1562 $params = ['title' => $function];
6a488035 1563 $entity = substr(basename($filename), 0, strlen(basename($filename)) - 8);
6c6e6187 1564 $params['extends'] = $entity ? $entity : 'Contact';
e255b57a 1565 $customGroup = $this->customGroupCreate($params);
7cf5bd55 1566 $customField = $this->customFieldCreate(['custom_group_id' => $customGroup['id'], 'label' => $function]);
80085473 1567 CRM_Core_PseudoConstant::flush();
6a488035 1568
7cf5bd55 1569 return ['custom_group_id' => $customGroup['id'], 'custom_field_id' => $customField['id']];
6a488035
TO
1570 }
1571
d215c0e1
MH
1572 /**
1573 * Create a custom group with a single text custom field, multi-select widget, with a variety of option values including upper and lower case.
1574 * See api_v3_SyntaxConformanceTest:testCustomDataGet for how to use this
1575 *
1576 * @param string $function
1577 * __FUNCTION__.
1578 * @param string $filename
1579 * $file __FILE__.
1580 *
1581 * @return array
1582 * ids of created objects
1583 */
1584 public function entityCustomGroupWithSingleStringMultiSelectFieldCreate($function, $filename) {
7cf5bd55 1585 $params = ['title' => $function];
d215c0e1
MH
1586 $entity = substr(basename($filename), 0, strlen(basename($filename)) - 8);
1587 $params['extends'] = $entity ? $entity : 'Contact';
e255b57a 1588 $customGroup = $this->customGroupCreate($params);
7cf5bd55 1589 $customField = $this->customFieldCreate(['custom_group_id' => $customGroup['id'], 'label' => $function, 'html_type' => 'Multi-Select', 'default_value' => 1]);
d215c0e1
MH
1590 CRM_Core_PseudoConstant::flush();
1591 $options = [
1592 'defaultValue' => 'Default Value',
1593 'lowercasevalue' => 'Lowercase Value',
1594 1 => 'Integer Value',
08703635 1595 'NULL' => 'NULL',
d215c0e1
MH
1596 ];
1597 $custom_field_params = ['sequential' => 1, 'id' => $customField['id']];
1598 $custom_field_api_result = $this->callAPISuccess('custom_field', 'get', $custom_field_params);
1599 $this->assertNotEmpty($custom_field_api_result['values'][0]['option_group_id']);
1600 $option_group_params = ['sequential' => 1, 'id' => $custom_field_api_result['values'][0]['option_group_id']];
1601 $option_group_result = $this->callAPISuccess('OptionGroup', 'get', $option_group_params);
1602 $this->assertNotEmpty($option_group_result['values'][0]['name']);
1603 foreach ($options as $option_value => $option_label) {
1604 $option_group_params = ['option_group_id' => $option_group_result['values'][0]['name'], 'value' => $option_value, 'label' => $option_label];
1605 $option_value_result = $this->callAPISuccess('OptionValue', 'create', $option_group_params);
1606 }
1607
7cf5bd55 1608 return [
1609 'custom_group_id' => $customGroup['id'],
1610 'custom_field_id' => $customField['id'],
1611 'custom_field_option_group_id' => $custom_field_api_result['values'][0]['option_group_id'],
1612 'custom_field_group_options' => $options,
1613 ];
d215c0e1
MH
1614 }
1615
6a488035 1616 /**
eceb18cc 1617 * Delete custom group.
6a488035 1618 *
8deefe64
EM
1619 * @param int $customGroupID
1620 *
1621 * @return array|int
6a488035 1622 */
00be9182 1623 public function customGroupDelete($customGroupID) {
6a488035 1624 $params['id'] = $customGroupID;
f6722559 1625 return $this->callAPISuccess('custom_group', 'delete', $params);
6a488035
TO
1626 }
1627
1628 /**
eceb18cc 1629 * Create custom field.
6a488035 1630 *
e16033b4
TO
1631 * @param array $params
1632 * (custom_group_id) is required.
7cf5bd55 1633 *
507d3b64 1634 * @return array
6a488035 1635 */
00be9182 1636 public function customFieldCreate($params) {
7cf5bd55 1637 $params = array_merge([
b422b715 1638 'label' => 'Custom Field',
6a488035
TO
1639 'data_type' => 'String',
1640 'html_type' => 'Text',
1641 'is_searchable' => 1,
1642 'is_active' => 1,
5c496c74 1643 'default_value' => 'defaultValue',
7cf5bd55 1644 ], $params);
6a488035 1645
5c496c74 1646 $result = $this->callAPISuccess('custom_field', 'create', $params);
507d3b64
EM
1647 // these 2 functions are called with force to flush static caches
1648 CRM_Core_BAO_CustomField::getTableColumnGroup($result['id'], 1);
1649 CRM_Core_Component::getEnabledComponents(1);
1650 return $result;
6a488035
TO
1651 }
1652
1653 /**
eceb18cc 1654 * Delete custom field.
6a488035
TO
1655 *
1656 * @param int $customFieldID
8deefe64
EM
1657 *
1658 * @return array|int
6a488035 1659 */
00be9182 1660 public function customFieldDelete($customFieldID) {
6a488035
TO
1661
1662 $params['id'] = $customFieldID;
f6722559 1663 return $this->callAPISuccess('custom_field', 'delete', $params);
6a488035
TO
1664 }
1665
1666 /**
eceb18cc 1667 * Create note.
6a488035 1668 *
100fef9d 1669 * @param int $cId
7cf5bd55 1670 *
a6c01b45 1671 * @return array
6a488035 1672 */
00be9182 1673 public function noteCreate($cId) {
7cf5bd55 1674 $params = [
6a488035
TO
1675 'entity_table' => 'civicrm_contact',
1676 'entity_id' => $cId,
1677 'note' => 'hello I am testing Note',
1678 'contact_id' => $cId,
1679 'modified_date' => date('Ymd'),
1680 'subject' => 'Test Note',
7cf5bd55 1681 ];
6a488035 1682
f6722559 1683 return $this->callAPISuccess('Note', 'create', $params);
6a488035
TO
1684 }
1685
07fd63f5 1686 /**
eceb18cc 1687 * Enable CiviCampaign Component.
d31d0888 1688 *
1689 * @param bool $reloadConfig
1690 * Force relaod config or not
07fd63f5 1691 */
d31d0888 1692 public function enableCiviCampaign($reloadConfig = TRUE) {
07fd63f5 1693 CRM_Core_BAO_ConfigSetting::enableComponent('CiviCampaign');
d31d0888 1694 if ($reloadConfig) {
1695 // force reload of config object
1696 $config = CRM_Core_Config::singleton(TRUE, TRUE);
1697 }
07fd63f5
E
1698 //flush cache by calling with reset
1699 $activityTypes = CRM_Core_PseudoConstant::activityType(TRUE, TRUE, TRUE, 'name', TRUE);
1700 }
1701
6a488035 1702 /**
eceb18cc 1703 * Create custom field with Option Values.
6a488035 1704 *
77b97be7 1705 * @param array $customGroup
e16033b4
TO
1706 * @param string $name
1707 * Name of custom field.
412585fb 1708 * @param array $extraParams
1709 * Additional parameters to pass through.
77b97be7
EM
1710 *
1711 * @return array|int
6a488035 1712 */
7cf5bd55 1713 public function customFieldOptionValueCreate($customGroup, $name, $extraParams = []) {
1714 $fieldParams = [
6a488035
TO
1715 'custom_group_id' => $customGroup['id'],
1716 'name' => 'test_custom_group',
1717 'label' => 'Country',
1718 'html_type' => 'Select',
1719 'data_type' => 'String',
1720 'weight' => 4,
1721 'is_required' => 1,
1722 'is_searchable' => 0,
1723 'is_active' => 1,
7cf5bd55 1724 ];
6a488035 1725
7cf5bd55 1726 $optionGroup = [
6a488035
TO
1727 'domain_id' => 1,
1728 'name' => 'option_group1',
1729 'label' => 'option_group_label1',
7cf5bd55 1730 ];
6a488035 1731
7cf5bd55 1732 $optionValue = [
1733 'option_label' => ['Label1', 'Label2'],
1734 'option_value' => ['value1', 'value2'],
1735 'option_name' => [$name . '_1', $name . '_2'],
1736 'option_weight' => [1, 2],
1737 'option_status' => [1, 1],
1738 ];
6a488035 1739
412585fb 1740 $params = array_merge($fieldParams, $optionGroup, $optionValue, $extraParams);
6a488035 1741
f6722559 1742 return $this->callAPISuccess('custom_field', 'create', $params);
6a488035
TO
1743 }
1744
4cbe18b8
EM
1745 /**
1746 * @param $entities
1747 *
1748 * @return bool
1749 */
00be9182 1750 public function confirmEntitiesDeleted($entities) {
6a488035
TO
1751 foreach ($entities as $entity) {
1752
7cf5bd55 1753 $result = $this->callAPISuccess($entity, 'Get', []);
6a488035
TO
1754 if ($result['error'] == 1 || $result['count'] > 0) {
1755 // > than $entity[0] to allow a value to be passed in? e.g. domain?
1756 return TRUE;
1757 }
1758 }
f0be539a 1759 return FALSE;
6a488035
TO
1760 }
1761
4cbe18b8 1762 /**
0fff773f 1763 * Quick clean by emptying tables created for the test.
1764 *
1765 * @param array $tablesToTruncate
4cbe18b8 1766 * @param bool $dropCustomValueTables
7cf5bd55 1767 *
ca8901aa 1768 * @throws \CRM_Core_Exception
4cbe18b8 1769 */
00be9182 1770 public function quickCleanup($tablesToTruncate, $dropCustomValueTables = FALSE) {
d67f1f28 1771 if ($this->tx) {
ca8901aa 1772 throw new \CRM_Core_Exception("CiviUnitTestCase: quickCleanup() is not compatible with useTransaction()");
d67f1f28 1773 }
6a488035 1774 if ($dropCustomValueTables) {
0fff773f 1775 $optionGroupResult = CRM_Core_DAO::executeQuery('SELECT option_group_id FROM civicrm_custom_field');
1776 while ($optionGroupResult->fetch()) {
6b051312 1777 // We have a test that sets the option_group_id for a custom group to that of 'activity_type'.
1778 // Then test tearDown deletes it. This is all mildly terrifying but for the context here we can be pretty
1779 // sure the low-numbered (50 is arbitrary) option groups are not ones to 'just delete' in a
1780 // generic cleanup routine.
1781 if (!empty($optionGroupResult->option_group_id) && $optionGroupResult->option_group_id > 50) {
0fff773f 1782 CRM_Core_DAO::executeQuery('DELETE FROM civicrm_option_group WHERE id = ' . $optionGroupResult->option_group_id);
1783 }
1784 }
6a488035
TO
1785 $tablesToTruncate[] = 'civicrm_custom_group';
1786 $tablesToTruncate[] = 'civicrm_custom_field';
1787 }
1788
0b6f58fa
ARW
1789 $tablesToTruncate = array_unique(array_merge($this->_tablesToTruncate, $tablesToTruncate));
1790
6a488035
TO
1791 CRM_Core_DAO::executeQuery("SET FOREIGN_KEY_CHECKS = 0;");
1792 foreach ($tablesToTruncate as $table) {
1793 $sql = "TRUNCATE TABLE $table";
1794 CRM_Core_DAO::executeQuery($sql);
1795 }
1796 CRM_Core_DAO::executeQuery("SET FOREIGN_KEY_CHECKS = 1;");
1797
1798 if ($dropCustomValueTables) {
1799 $dbName = self::getDBName();
1800 $query = "
1801SELECT TABLE_NAME as tableName
1802FROM INFORMATION_SCHEMA.TABLES
1803WHERE TABLE_SCHEMA = '{$dbName}'
1804AND ( TABLE_NAME LIKE 'civicrm_value_%' )
1805";
1806
1807 $tableDAO = CRM_Core_DAO::executeQuery($query);
1808 while ($tableDAO->fetch()) {
1809 $sql = "DROP TABLE {$tableDAO->tableName}";
1810 CRM_Core_DAO::executeQuery($sql);
1811 }
1812 }
1813 }
1814
b38530f2
EM
1815 /**
1816 * Clean up financial entities after financial tests (so we remember to get all the tables :-))
5a433c56 1817 *
1818 * @throws \CRM_Core_Exception
b38530f2 1819 */
00be9182 1820 public function quickCleanUpFinancialEntities() {
7cf5bd55 1821 $tablesToTruncate = [
a380f4a0
EM
1822 'civicrm_activity',
1823 'civicrm_activity_contact',
b38530f2 1824 'civicrm_contribution',
39d632fd 1825 'civicrm_contribution_soft',
945f423d 1826 'civicrm_contribution_product',
b38530f2 1827 'civicrm_financial_trxn',
39d632fd 1828 'civicrm_financial_item',
b38530f2
EM
1829 'civicrm_contribution_recur',
1830 'civicrm_line_item',
1831 'civicrm_contribution_page',
1832 'civicrm_payment_processor',
1833 'civicrm_entity_financial_trxn',
1834 'civicrm_membership',
1835 'civicrm_membership_type',
1836 'civicrm_membership_payment',
cab024d4 1837 'civicrm_membership_log',
f9342903 1838 'civicrm_membership_block',
b38530f2
EM
1839 'civicrm_event',
1840 'civicrm_participant',
1841 'civicrm_participant_payment',
1842 'civicrm_pledge',
4fa732d2 1843 'civicrm_pcp_block',
1844 'civicrm_pcp',
01ead735 1845 'civicrm_pledge_block',
294cc627 1846 'civicrm_pledge_payment',
1cf3c2b1 1847 'civicrm_price_set_entity',
108ff21a
EM
1848 'civicrm_price_field_value',
1849 'civicrm_price_field',
7cf5bd55 1850 ];
b38530f2 1851 $this->quickCleanup($tablesToTruncate);
4278b952 1852 CRM_Core_DAO::executeQuery("DELETE FROM civicrm_membership_status WHERE name NOT IN('New', 'Current', 'Grace', 'Expired', 'Pending', 'Cancelled', 'Deceased')");
108ff21a 1853 $this->restoreDefaultPriceSetConfig();
9fbf312f 1854 $this->disableTaxAndInvoicing();
83644f47 1855 $this->setCurrencySeparators(',');
1856 CRM_Core_PseudoConstant::flush('taxRates');
7a3b0ca3 1857 System::singleton()->flushProcessors();
bc438a52 1858 // @fixme this parameter is leaking - it should not be defined as a class static
1859 // but for now we just handle in tear down.
1860 CRM_Contribute_BAO_Query::$_contribOrSoftCredit = 'only contribs';
108ff21a
EM
1861 }
1862
5a433c56 1863 /**
1864 * Reset the price set config so results exist.
1865 */
00be9182 1866 public function restoreDefaultPriceSetConfig() {
cdd71d6b 1867 CRM_Core_DAO::executeQuery("DELETE FROM civicrm_price_set WHERE name NOT IN('default_contribution_amount', 'default_membership_type_amount')");
1868 CRM_Core_DAO::executeQuery("UPDATE civicrm_price_set SET id = 1 WHERE name ='default_contribution_amount'");
108ff21a 1869 CRM_Core_DAO::executeQuery("INSERT INTO `civicrm_price_field` (`id`, `price_set_id`, `name`, `label`, `html_type`, `is_enter_qty`, `help_pre`, `help_post`, `weight`, `is_display_amounts`, `options_per_line`, `is_active`, `is_required`, `active_on`, `expire_on`, `javascript`, `visibility_id`) VALUES (1, 1, 'contribution_amount', 'Contribution Amount', 'Text', 0, NULL, NULL, 1, 1, 1, 1, 1, NULL, NULL, NULL, 1)");
5afce5ad 1870 CRM_Core_DAO::executeQuery("INSERT INTO `civicrm_price_field_value` (`id`, `price_field_id`, `name`, `label`, `description`, `amount`, `count`, `max_value`, `weight`, `membership_type_id`, `membership_num_terms`, `is_default`, `is_active`, `financial_type_id`, `non_deductible_amount`) VALUES (1, 1, 'contribution_amount', 'Contribution Amount', NULL, '1', NULL, NULL, 1, NULL, NULL, 0, 1, 1, 0.00)");
b38530f2 1871 }
39b959db 1872
5a433c56 1873 /**
1874 * Recreate default membership types.
1875 */
1876 public function restoreMembershipTypes() {
1877 CRM_Core_DAO::executeQuery(
1878 "REPLACE INTO civicrm_membership_type
1879 (id, domain_id, name, description, member_of_contact_id, financial_type_id, minimum_fee, duration_unit, duration_interval, period_type, fixed_period_start_day, fixed_period_rollover_day, relationship_type_id, relationship_direction, visibility, weight, is_active)
1880VALUES
1881 (1, 1, 'General', 'Regular annual membership.', 1, 2, 100.00, 'year', 2, 'rolling', NULL, NULL, 7, 'b_a', 'Public', 1, 1),
1882 (2, 1, 'Student', 'Discount membership for full-time students.', 1, 2, 50.00, 'year', 1, 'rolling', NULL, NULL, NULL, NULL, 'Public', 2, 1),
1883 (3, 1, 'Lifetime', 'Lifetime membership.', 1, 2, 1200.00, 'lifetime', 1, 'rolling', NULL, NULL, 7, 'b_a', 'Admin', 3, 1);
1884 ");
1885 }
1886
6a488035
TO
1887 /*
1888 * Function does a 'Get' on the entity & compares the fields in the Params with those returned
1889 * Default behaviour is to also delete the entity
e16033b4 1890 * @param array $params
e4f46be0 1891 * Params array to check against.
e16033b4
TO
1892 * @param int $id
1893 * Id of the entity concerned.
1894 * @param string $entity
1895 * Name of entity concerned (e.g. membership).
1896 * @param bool $delete
1897 * Should the entity be deleted as part of this check.
1898 * @param string $errorText
1899 * Text to print on error.
6a488035 1900 */
39b959db 1901
4cbe18b8 1902 /**
c490a46a 1903 * @param array $params
100fef9d 1904 * @param int $id
4cbe18b8
EM
1905 * @param $entity
1906 * @param int $delete
1907 * @param string $errorText
1908 *
b086dc3a 1909 * @throws CRM_Core_Exception
4cbe18b8 1910 */
00be9182 1911 public function getAndCheck($params, $id, $entity, $delete = 1, $errorText = '') {
6a488035 1912
7cf5bd55 1913 $result = $this->callAPISuccessGetSingle($entity, [
6a488035 1914 'id' => $id,
7cf5bd55 1915 ]);
6a488035
TO
1916
1917 if ($delete) {
7cf5bd55 1918 $this->callAPISuccess($entity, 'Delete', [
6a488035 1919 'id' => $id,
7cf5bd55 1920 ]);
6a488035 1921 }
7cf5bd55 1922 $dateFields = $keys = $dateTimeFields = [];
1923 $fields = $this->callAPISuccess($entity, 'getfields', ['version' => 3, 'action' => 'get']);
6a488035
TO
1924 foreach ($fields['values'] as $field => $settings) {
1925 if (array_key_exists($field, $result)) {
e255b57a 1926 $keys[CRM_Utils_Array::value('name', $settings, $field)] = $field;
6a488035
TO
1927 }
1928 else {
e255b57a 1929 $keys[CRM_Utils_Array::value('name', $settings, $field)] = CRM_Utils_Array::value('name', $settings, $field);
6a488035 1930 }
849ac823 1931 $type = $settings['type'] ?? NULL;
afd404ea 1932 if ($type == CRM_Utils_Type::T_DATE) {
1933 $dateFields[] = $settings['name'];
1934 // we should identify both real names & unique names as dates
5896d037 1935 if ($field != $settings['name']) {
afd404ea 1936 $dateFields[] = $field;
1937 }
1938 }
5896d037 1939 if ($type == CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME) {
afd404ea 1940 $dateTimeFields[] = $settings['name'];
1941 // we should identify both real names & unique names as dates
5896d037 1942 if ($field != $settings['name']) {
afd404ea 1943 $dateTimeFields[] = $field;
1944 }
6a488035
TO
1945 }
1946 }
1947
1948 if (strtolower($entity) == 'contribution') {
1949 $params['receive_date'] = date('Y-m-d', strtotime($params['receive_date']));
1950 // this is not returned in id format
1951 unset($params['payment_instrument_id']);
1952 $params['contribution_source'] = $params['source'];
1953 unset($params['source']);
1954 }
1955
1956 foreach ($params as $key => $value) {
afd404ea 1957 if ($key == 'version' || substr($key, 0, 3) == 'api' || !array_key_exists($keys[$key], $result)) {
6a488035
TO
1958 continue;
1959 }
1960 if (in_array($key, $dateFields)) {
1961 $value = date('Y-m-d', strtotime($value));
1962 $result[$key] = date('Y-m-d', strtotime($result[$key]));
1963 }
afd404ea 1964 if (in_array($key, $dateTimeFields)) {
1965 $value = date('Y-m-d H:i:s', strtotime($value));
a72cec08 1966 $result[$keys[$key]] = date('Y-m-d H:i:s', strtotime(CRM_Utils_Array::value($keys[$key], $result, CRM_Utils_Array::value($key, $result))));
afd404ea 1967 }
1968 $this->assertEquals($value, $result[$keys[$key]], $key . " GetandCheck function determines that for key {$key} value: $value doesn't match " . print_r($result[$keys[$key]], TRUE) . $errorText);
6a488035
TO
1969 }
1970 }
1971
1972 /**
eceb18cc 1973 * Get formatted values in the actual and expected result.
7cf5bd55 1974 *
e16033b4
TO
1975 * @param array $actual
1976 * Actual calculated values.
1977 * @param array $expected
1978 * Expected values.
6a488035 1979 */
00be9182 1980 public function checkArrayEquals(&$actual, &$expected) {
6a488035
TO
1981 self::unsetId($actual);
1982 self::unsetId($expected);
25115a3e 1983 $this->assertEquals($expected, $actual);
6a488035
TO
1984 }
1985
1986 /**
100fef9d 1987 * Unset the key 'id' from the array
7cf5bd55 1988 *
e16033b4
TO
1989 * @param array $unformattedArray
1990 * The array from which the 'id' has to be unset.
6a488035 1991 */
00be9182 1992 public static function unsetId(&$unformattedArray) {
7cf5bd55 1993 $formattedArray = [];
6a488035
TO
1994 if (array_key_exists('id', $unformattedArray)) {
1995 unset($unformattedArray['id']);
1996 }
a7488080 1997 if (!empty($unformattedArray['values']) && is_array($unformattedArray['values'])) {
6a488035 1998 foreach ($unformattedArray['values'] as $key => $value) {
6c6e6187 1999 if (is_array($value)) {
6a488035
TO
2000 foreach ($value as $k => $v) {
2001 if ($k == 'id') {
2002 unset($value[$k]);
2003 }
2004 }
2005 }
2006 elseif ($key == 'id') {
2007 $unformattedArray[$key];
2008 }
7cf5bd55 2009 $formattedArray = [$value];
6a488035
TO
2010 }
2011 $unformattedArray['values'] = $formattedArray;
2012 }
2013 }
2014
2015 /**
2016 * Helper to enable/disable custom directory support
2017 *
e16033b4
TO
2018 * @param array $customDirs
2019 * With members:.
6a488035
TO
2020 * 'php_path' Set to TRUE to use the default, FALSE or "" to disable support, or a string path to use another path
2021 * 'template_path' Set to TRUE to use the default, FALSE or "" to disable support, or a string path to use another path
2022 */
00be9182 2023 public function customDirectories($customDirs) {
6a488035
TO
2024 $config = CRM_Core_Config::singleton();
2025
2026 if (empty($customDirs['php_path']) || $customDirs['php_path'] === FALSE) {
2027 unset($config->customPHPPathDir);
2028 }
2029 elseif ($customDirs['php_path'] === TRUE) {
2030 $config->customPHPPathDir = dirname(dirname(__FILE__)) . '/custom_directories/php/';
2031 }
2032 else {
2033 $config->customPHPPathDir = $php_path;
2034 }
2035
2036 if (empty($customDirs['template_path']) || $customDirs['template_path'] === FALSE) {
2037 unset($config->customTemplateDir);
2038 }
2039 elseif ($customDirs['template_path'] === TRUE) {
2040 $config->customTemplateDir = dirname(dirname(__FILE__)) . '/custom_directories/templates/';
2041 }
2042 else {
2043 $config->customTemplateDir = $template_path;
2044 }
2045 }
2046
2047 /**
eceb18cc 2048 * Generate a temporary folder.
6a488035 2049 *
2a6da8d7 2050 * @param string $prefix
7cf5bd55 2051 *
a6c01b45 2052 * @return string
6a488035 2053 */
00be9182 2054 public function createTempDir($prefix = 'test-') {
6a488035
TO
2055 $tempDir = CRM_Utils_File::tempdir($prefix);
2056 $this->tempDirs[] = $tempDir;
2057 return $tempDir;
2058 }
2059
00be9182 2060 public function cleanTempDirs() {
6a488035
TO
2061 if (!is_array($this->tempDirs)) {
2062 // fix test errors where this is not set
2063 return;
2064 }
2065 foreach ($this->tempDirs as $tempDir) {
2066 if (is_dir($tempDir)) {
2067 CRM_Utils_File::cleanDir($tempDir, TRUE, FALSE);
2068 }
2069 }
2070 }
2071
2072 /**
eceb18cc 2073 * Temporarily replace the singleton extension with a different one.
7cf5bd55 2074 *
1e1fdcf6 2075 * @param \CRM_Extension_System $system
6a488035 2076 */
00be9182 2077 public function setExtensionSystem(CRM_Extension_System $system) {
6a488035
TO
2078 if ($this->origExtensionSystem == NULL) {
2079 $this->origExtensionSystem = CRM_Extension_System::singleton();
2080 }
2081 CRM_Extension_System::setSingleton($this->origExtensionSystem);
2082 }
2083
00be9182 2084 public function unsetExtensionSystem() {
6a488035
TO
2085 if ($this->origExtensionSystem !== NULL) {
2086 CRM_Extension_System::setSingleton($this->origExtensionSystem);
2087 $this->origExtensionSystem = NULL;
2088 }
2089 }
f17d75bb 2090
076d8c82
TO
2091 /**
2092 * Temporarily alter the settings-metadata to add a mock setting.
2093 *
2094 * WARNING: The setting metadata will disappear on the next cache-clear.
2095 *
2096 * @param $extras
7cf5bd55 2097 *
076d8c82
TO
2098 * @return void
2099 */
00be9182 2100 public function setMockSettingsMetaData($extras) {
5896d037
TO
2101 CRM_Utils_Hook::singleton()
2102 ->setHook('civicrm_alterSettingsMetaData', function (&$metadata, $domainId, $profile) use ($extras) {
2103 $metadata = array_merge($metadata, $extras);
2104 });
076d8c82 2105
1209c8a7
CW
2106 Civi::service('settings_manager')->flush();
2107
7cf5bd55 2108 $fields = $this->callAPISuccess('setting', 'getfields', []);
076d8c82
TO
2109 foreach ($extras as $key => $spec) {
2110 $this->assertNotEmpty($spec['title']);
2111 $this->assertEquals($spec['title'], $fields['values'][$key]['title']);
2112 }
2113 }
2114
4cbe18b8 2115 /**
100fef9d 2116 * @param string $name
4cbe18b8 2117 */
00be9182 2118 public function financialAccountDelete($name) {
f17d75bb
PN
2119 $financialAccount = new CRM_Financial_DAO_FinancialAccount();
2120 $financialAccount->name = $name;
5896d037 2121 if ($financialAccount->find(TRUE)) {
f17d75bb
PN
2122 $entityFinancialType = new CRM_Financial_DAO_EntityFinancialAccount();
2123 $entityFinancialType->financial_account_id = $financialAccount->id;
2124 $entityFinancialType->delete();
2125 $financialAccount->delete();
2126 }
2127 }
fb32de45 2128
2b7d3f8a 2129 /**
2130 * FIXME: something NULLs $GLOBALS['_HTML_QuickForm_registered_rules'] when the tests are ran all together
2131 * (NB unclear if this is still required)
2132 */
00be9182 2133 public function _sethtmlGlobals() {
7cf5bd55 2134 $GLOBALS['_HTML_QuickForm_registered_rules'] = [
2135 'required' => [
2b7d3f8a 2136 'html_quickform_rule_required',
21dfd5f5 2137 'HTML/QuickForm/Rule/Required.php',
7cf5bd55 2138 ],
2139 'maxlength' => [
2b7d3f8a 2140 'html_quickform_rule_range',
21dfd5f5 2141 'HTML/QuickForm/Rule/Range.php',
7cf5bd55 2142 ],
2143 'minlength' => [
2b7d3f8a 2144 'html_quickform_rule_range',
21dfd5f5 2145 'HTML/QuickForm/Rule/Range.php',
7cf5bd55 2146 ],
2147 'rangelength' => [
2b7d3f8a 2148 'html_quickform_rule_range',
21dfd5f5 2149 'HTML/QuickForm/Rule/Range.php',
7cf5bd55 2150 ],
2151 'email' => [
2b7d3f8a 2152 'html_quickform_rule_email',
21dfd5f5 2153 'HTML/QuickForm/Rule/Email.php',
7cf5bd55 2154 ],
2155 'regex' => [
2b7d3f8a 2156 'html_quickform_rule_regex',
21dfd5f5 2157 'HTML/QuickForm/Rule/Regex.php',
7cf5bd55 2158 ],
2159 'lettersonly' => [
2b7d3f8a 2160 'html_quickform_rule_regex',
21dfd5f5 2161 'HTML/QuickForm/Rule/Regex.php',
7cf5bd55 2162 ],
2163 'alphanumeric' => [
2b7d3f8a 2164 'html_quickform_rule_regex',
21dfd5f5 2165 'HTML/QuickForm/Rule/Regex.php',
7cf5bd55 2166 ],
2167 'numeric' => [
2b7d3f8a 2168 'html_quickform_rule_regex',
21dfd5f5 2169 'HTML/QuickForm/Rule/Regex.php',
7cf5bd55 2170 ],
2171 'nopunctuation' => [
2b7d3f8a 2172 'html_quickform_rule_regex',
21dfd5f5 2173 'HTML/QuickForm/Rule/Regex.php',
7cf5bd55 2174 ],
2175 'nonzero' => [
2b7d3f8a 2176 'html_quickform_rule_regex',
21dfd5f5 2177 'HTML/QuickForm/Rule/Regex.php',
7cf5bd55 2178 ],
2179 'callback' => [
2b7d3f8a 2180 'html_quickform_rule_callback',
21dfd5f5 2181 'HTML/QuickForm/Rule/Callback.php',
7cf5bd55 2182 ],
2183 'compare' => [
2b7d3f8a 2184 'html_quickform_rule_compare',
21dfd5f5 2185 'HTML/QuickForm/Rule/Compare.php',
7cf5bd55 2186 ],
2187 ];
2b7d3f8a 2188 // FIXME: …ditto for $GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES']
7cf5bd55 2189 $GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'] = [
2190 'group' => [
2b7d3f8a 2191 'HTML/QuickForm/group.php',
21dfd5f5 2192 'HTML_QuickForm_group',
7cf5bd55 2193 ],
2194 'hidden' => [
2b7d3f8a 2195 'HTML/QuickForm/hidden.php',
21dfd5f5 2196 'HTML_QuickForm_hidden',
7cf5bd55 2197 ],
2198 'reset' => [
2b7d3f8a 2199 'HTML/QuickForm/reset.php',
21dfd5f5 2200 'HTML_QuickForm_reset',
7cf5bd55 2201 ],
2202 'checkbox' => [
2b7d3f8a 2203 'HTML/QuickForm/checkbox.php',
21dfd5f5 2204 'HTML_QuickForm_checkbox',
7cf5bd55 2205 ],
2206 'file' => [
2b7d3f8a 2207 'HTML/QuickForm/file.php',
21dfd5f5 2208 'HTML_QuickForm_file',
7cf5bd55 2209 ],
2210 'image' => [
2b7d3f8a 2211 'HTML/QuickForm/image.php',
21dfd5f5 2212 'HTML_QuickForm_image',
7cf5bd55 2213 ],
2214 'password' => [
2b7d3f8a 2215 'HTML/QuickForm/password.php',
21dfd5f5 2216 'HTML_QuickForm_password',
7cf5bd55 2217 ],
2218 'radio' => [
2b7d3f8a 2219 'HTML/QuickForm/radio.php',
21dfd5f5 2220 'HTML_QuickForm_radio',
7cf5bd55 2221 ],
2222 'button' => [
2b7d3f8a 2223 'HTML/QuickForm/button.php',
21dfd5f5 2224 'HTML_QuickForm_button',
7cf5bd55 2225 ],
2226 'submit' => [
2b7d3f8a 2227 'HTML/QuickForm/submit.php',
21dfd5f5 2228 'HTML_QuickForm_submit',
7cf5bd55 2229 ],
2230 'select' => [
2b7d3f8a 2231 'HTML/QuickForm/select.php',
21dfd5f5 2232 'HTML_QuickForm_select',
7cf5bd55 2233 ],
2234 'hiddenselect' => [
2b7d3f8a 2235 'HTML/QuickForm/hiddenselect.php',
21dfd5f5 2236 'HTML_QuickForm_hiddenselect',
7cf5bd55 2237 ],
2238 'text' => [
2b7d3f8a 2239 'HTML/QuickForm/text.php',
21dfd5f5 2240 'HTML_QuickForm_text',
7cf5bd55 2241 ],
2242 'textarea' => [
2b7d3f8a 2243 'HTML/QuickForm/textarea.php',
21dfd5f5 2244 'HTML_QuickForm_textarea',
7cf5bd55 2245 ],
2246 'fckeditor' => [
2b7d3f8a 2247 'HTML/QuickForm/fckeditor.php',
21dfd5f5 2248 'HTML_QuickForm_FCKEditor',
7cf5bd55 2249 ],
2250 'tinymce' => [
2b7d3f8a 2251 'HTML/QuickForm/tinymce.php',
21dfd5f5 2252 'HTML_QuickForm_TinyMCE',
7cf5bd55 2253 ],
2254 'dojoeditor' => [
2b7d3f8a 2255 'HTML/QuickForm/dojoeditor.php',
21dfd5f5 2256 'HTML_QuickForm_dojoeditor',
7cf5bd55 2257 ],
2258 'link' => [
2b7d3f8a 2259 'HTML/QuickForm/link.php',
21dfd5f5 2260 'HTML_QuickForm_link',
7cf5bd55 2261 ],
2262 'advcheckbox' => [
2b7d3f8a 2263 'HTML/QuickForm/advcheckbox.php',
21dfd5f5 2264 'HTML_QuickForm_advcheckbox',
7cf5bd55 2265 ],
2266 'date' => [
2b7d3f8a 2267 'HTML/QuickForm/date.php',
21dfd5f5 2268 'HTML_QuickForm_date',
7cf5bd55 2269 ],
2270 'static' => [
2b7d3f8a 2271 'HTML/QuickForm/static.php',
21dfd5f5 2272 'HTML_QuickForm_static',
7cf5bd55 2273 ],
2274 'header' => [
2b7d3f8a 2275 'HTML/QuickForm/header.php',
21dfd5f5 2276 'HTML_QuickForm_header',
7cf5bd55 2277 ],
2278 'html' => [
2b7d3f8a 2279 'HTML/QuickForm/html.php',
21dfd5f5 2280 'HTML_QuickForm_html',
7cf5bd55 2281 ],
2282 'hierselect' => [
2b7d3f8a 2283 'HTML/QuickForm/hierselect.php',
21dfd5f5 2284 'HTML_QuickForm_hierselect',
7cf5bd55 2285 ],
2286 'autocomplete' => [
2b7d3f8a 2287 'HTML/QuickForm/autocomplete.php',
21dfd5f5 2288 'HTML_QuickForm_autocomplete',
7cf5bd55 2289 ],
2290 'xbutton' => [
2b7d3f8a 2291 'HTML/QuickForm/xbutton.php',
21dfd5f5 2292 'HTML_QuickForm_xbutton',
7cf5bd55 2293 ],
2294 'advmultiselect' => [
2b7d3f8a 2295 'HTML/QuickForm/advmultiselect.php',
21dfd5f5 2296 'HTML_QuickForm_advmultiselect',
7cf5bd55 2297 ],
2298 ];
2b7d3f8a 2299 }
2300
48e399ac
EM
2301 /**
2302 * Set up an acl allowing contact to see 2 specified groups
c206647d 2303 * - $this->_permissionedGroup & $this->_permissionedDisabledGroup
48e399ac 2304 *
c206647d 2305 * You need to have pre-created these groups & created the user e.g
48e399ac
EM
2306 * $this->createLoggedInUser();
2307 * $this->_permissionedDisabledGroup = $this->groupCreate(array('title' => 'pick-me-disabled', 'is_active' => 0, 'name' => 'pick-me-disabled'));
2308 * $this->_permissionedGroup = $this->groupCreate(array('title' => 'pick-me-active', 'is_active' => 1, 'name' => 'pick-me-active'));
ea3ddccf 2309 *
2310 * @param bool $isProfile
48e399ac 2311 */
181f536c 2312 public function setupACL($isProfile = FALSE) {
aaac0e0b
EM
2313 global $_REQUEST;
2314 $_REQUEST = $this->_params;
108ff21a 2315
7cf5bd55 2316 CRM_Core_Config::singleton()->userPermissionClass->permissions = ['access CiviCRM'];
2317 $optionGroupID = $this->callAPISuccessGetValue('option_group', ['return' => 'id', 'name' => 'acl_role']);
e5720c45
SL
2318 $ov = new CRM_Core_DAO_OptionValue();
2319 $ov->option_group_id = $optionGroupID;
2320 $ov->value = 55;
2321 if ($ov->find(TRUE)) {
2322 CRM_Core_DAO::executeQuery("DELETE FROM civicrm_option_value WHERE id = {$ov->id}");
2323 }
7cf5bd55 2324 $optionValue = $this->callAPISuccess('option_value', 'create', [
5896d037 2325 'option_group_id' => $optionGroupID,
48e399ac
EM
2326 'label' => 'pick me',
2327 'value' => 55,
7cf5bd55 2328 ]);
48e399ac 2329
48e399ac
EM
2330 CRM_Core_DAO::executeQuery("
2331 TRUNCATE civicrm_acl_cache
2332 ");
2333
2334 CRM_Core_DAO::executeQuery("
2335 TRUNCATE civicrm_acl_contact_cache
2336 ");
2337
48e399ac
EM
2338 CRM_Core_DAO::executeQuery("
2339 INSERT INTO civicrm_acl_entity_role (
181f536c 2340 `acl_role_id`, `entity_table`, `entity_id`, `is_active`
2341 ) VALUES (55, 'civicrm_group', {$this->_permissionedGroup}, 1);
48e399ac
EM
2342 ");
2343
181f536c 2344 if ($isProfile) {
2345 CRM_Core_DAO::executeQuery("
2346 INSERT INTO civicrm_acl (
2347 `name`, `entity_table`, `entity_id`, `operation`, `object_table`, `object_id`, `is_active`
2348 )
2349 VALUES (
2350 'view picked', 'civicrm_acl_role', 55, 'Edit', 'civicrm_uf_group', 0, 1
2351 );
2352 ");
2353 }
2354 else {
2355 CRM_Core_DAO::executeQuery("
2356 INSERT INTO civicrm_acl (
2357 `name`, `entity_table`, `entity_id`, `operation`, `object_table`, `object_id`, `is_active`
2358 )
2359 VALUES (
2360 'view picked', 'civicrm_group', $this->_permissionedGroup , 'Edit', 'civicrm_saved_search', {$this->_permissionedGroup}, 1
2361 );
2362 ");
2363
2364 CRM_Core_DAO::executeQuery("
2365 INSERT INTO civicrm_acl (
2366 `name`, `entity_table`, `entity_id`, `operation`, `object_table`, `object_id`, `is_active`
2367 )
2368 VALUES (
2369 'view picked', 'civicrm_group', $this->_permissionedGroup, 'Edit', 'civicrm_saved_search', {$this->_permissionedDisabledGroup}, 1
2370 );
2371 ");
181f536c 2372 }
48e399ac 2373
48e399ac 2374 $this->_loggedInUser = CRM_Core_Session::singleton()->get('userID');
7cf5bd55 2375 $this->callAPISuccess('group_contact', 'create', [
48e399ac
EM
2376 'group_id' => $this->_permissionedGroup,
2377 'contact_id' => $this->_loggedInUser,
7cf5bd55 2378 ]);
08a2ea5e 2379
2380 if (!$isProfile) {
2381 //flush cache
2382 CRM_ACL_BAO_Cache::resetCache();
340c24cc 2383 CRM_ACL_API::groupPermission('whatever', 9999, NULL, 'civicrm_saved_search', NULL, NULL);
08a2ea5e 2384 }
48e399ac
EM
2385 }
2386
cab024d4 2387 /**
100fef9d 2388 * Alter default price set so that the field numbers are not all 1 (hiding errors)
cab024d4 2389 */
00be9182 2390 public function offsetDefaultPriceSet() {
7cf5bd55 2391 $contributionPriceSet = $this->callAPISuccess('price_set', 'getsingle', ['name' => 'default_contribution_amount']);
cab024d4 2392 $firstID = $contributionPriceSet['id'];
7cf5bd55 2393 $this->callAPISuccess('price_set', 'create', [
92915c55
TO
2394 'id' => $contributionPriceSet['id'],
2395 'is_active' => 0,
2396 'name' => 'old',
7cf5bd55 2397 ]);
cab024d4
EM
2398 unset($contributionPriceSet['id']);
2399 $newPriceSet = $this->callAPISuccess('price_set', 'create', $contributionPriceSet);
7cf5bd55 2400 $priceField = $this->callAPISuccess('price_field', 'getsingle', [
92915c55 2401 'price_set_id' => $firstID,
7cf5bd55 2402 'options' => ['limit' => 1],
2403 ]);
cab024d4
EM
2404 unset($priceField['id']);
2405 $priceField['price_set_id'] = $newPriceSet['id'];
2406 $newPriceField = $this->callAPISuccess('price_field', 'create', $priceField);
7cf5bd55 2407 $priceFieldValue = $this->callAPISuccess('price_field_value', 'getsingle', [
92915c55
TO
2408 'price_set_id' => $firstID,
2409 'sequential' => 1,
7cf5bd55 2410 'options' => ['limit' => 1],
2411 ]);
cab024d4
EM
2412
2413 unset($priceFieldValue['id']);
2414 //create some padding to use up ids
2415 $this->callAPISuccess('price_field_value', 'create', $priceFieldValue);
2416 $this->callAPISuccess('price_field_value', 'create', $priceFieldValue);
7cf5bd55 2417 $this->callAPISuccess('price_field_value', 'create', array_merge($priceFieldValue, ['price_field_id' => $newPriceField['id']]));
cab024d4
EM
2418 }
2419
4aef704e 2420 /**
eceb18cc 2421 * Create an instance of the paypal processor.
7cf5bd55 2422 *
4aef704e 2423 * @todo this isn't a great place to put it - but really it belongs on a class that extends
2424 * this parent class & we don't have a structure for that yet
2425 * There is another function to this effect on the PaypalPro test but it appears to be silently failing
e4f46be0 2426 * & the best protection against that is the functions this class affords
7cf5bd55 2427 *
1e1fdcf6 2428 * @param array $params
7cf5bd55 2429 *
79d7553f 2430 * @return int $result['id'] payment processor id
4aef704e 2431 */
7cf5bd55 2432 public function paymentProcessorCreate($params = []) {
2433 $params = array_merge([
39b959db
SL
2434 'name' => 'demo',
2435 'domain_id' => CRM_Core_Config::domainID(),
2436 'payment_processor_type_id' => 'PayPal',
2437 'is_active' => 1,
2438 'is_default' => 0,
2439 'is_test' => 1,
2440 'user_name' => 'sunil._1183377782_biz_api1.webaccess.co.in',
2441 'password' => '1183377788',
2442 'signature' => 'APixCoQ-Zsaj-u3IH7mD5Do-7HUqA9loGnLSzsZga9Zr-aNmaJa3WGPH',
2443 'url_site' => 'https://www.sandbox.paypal.com/',
2444 'url_api' => 'https://api-3t.sandbox.paypal.com/',
2445 'url_button' => 'https://www.paypal.com/en_US/i/btn/btn_xpressCheckout.gif',
2446 'class_name' => 'Payment_PayPalImpl',
2447 'billing_mode' => 3,
2448 'financial_type_id' => 1,
2449 'financial_account_id' => 12,
2450 // Credit card = 1 so can pass 'by accident'.
2451 'payment_instrument_id' => 'Debit Card',
7cf5bd55 2452 ], $params);
5896d037 2453 if (!is_numeric($params['payment_processor_type_id'])) {
4aef704e 2454 // really the api should handle this through getoptions but it's not exactly api call so lets just sort it
2455 //here
7cf5bd55 2456 $params['payment_processor_type_id'] = $this->callAPISuccess('payment_processor_type', 'getvalue', [
4aef704e 2457 'name' => $params['payment_processor_type_id'],
2458 'return' => 'id',
7cf5bd55 2459 ], 'integer');
4aef704e 2460 }
2461 $result = $this->callAPISuccess('payment_processor', 'create', $params);
2462 return $result['id'];
2463 }
a9ac877b 2464
0dbefed3 2465 /**
eceb18cc 2466 * Set up initial recurring payment allowing subsequent IPN payments.
9f68fe61
MW
2467 *
2468 * @param array $recurParams (Optional)
2469 * @param array $contributionParams (Optional)
7aeb7f06 2470 *
2471 * @throws \CRM_Core_Exception
0dbefed3 2472 */
9f68fe61
MW
2473 public function setupRecurringPaymentProcessorTransaction($recurParams = [], $contributionParams = []) {
2474 $contributionParams = array_merge([
39b959db
SL
2475 'total_amount' => '200',
2476 'invoice_id' => $this->_invoiceID,
2477 'financial_type_id' => 'Donation',
2478 'contribution_status_id' => 'Pending',
2479 'contact_id' => $this->_contactID,
2480 'contribution_page_id' => $this->_contributionPageID,
2481 'payment_processor_id' => $this->_paymentProcessorID,
2482 'is_test' => 0,
f03241be 2483 'receive_date' => '2019-07-25 07:34:23',
39b959db
SL
2484 'skipCleanMoney' => TRUE,
2485 ], $contributionParams);
7cf5bd55 2486 $contributionRecur = $this->callAPISuccess('contribution_recur', 'create', array_merge([
0dbefed3
EM
2487 'contact_id' => $this->_contactID,
2488 'amount' => 1000,
2489 'sequential' => 1,
2490 'installments' => 5,
2491 'frequency_unit' => 'Month',
2492 'frequency_interval' => 1,
2493 'invoice_id' => $this->_invoiceID,
2494 'contribution_status_id' => 2,
481312d9 2495 'payment_processor_id' => $this->_paymentProcessorID,
2496 // processor provided ID - use contact ID as proxy.
2497 'processor_id' => $this->_contactID,
0dea0c7c 2498 'api.Order.create' => $contributionParams,
2499 ], $recurParams))['values'][0];
0dbefed3 2500 $this->_contributionRecurID = $contributionRecur['id'];
0dea0c7c 2501 $this->_contributionID = $contributionRecur['api.Order.create']['id'];
7aeb7f06 2502 $this->ids['Contribution'][0] = $this->_contributionID;
0dbefed3 2503 }
a86d27fc 2504
a9ac877b 2505 /**
100fef9d 2506 * We don't have a good way to set up a recurring contribution with a membership so let's just do one then alter it
b3c283b4
MW
2507 *
2508 * @param array $params Optionally modify params for membership/recur (duration_unit/frequency_unit)
7aeb7f06 2509 *
2510 * @throws \CRM_Core_Exception
a9ac877b 2511 */
7cf5bd55 2512 public function setupMembershipRecurringPaymentProcessorTransaction($params = []) {
2513 $membershipParams = $recurParams = [];
b3c283b4
MW
2514 if (!empty($params['duration_unit'])) {
2515 $membershipParams['duration_unit'] = $params['duration_unit'];
2516 }
2517 if (!empty($params['frequency_unit'])) {
2518 $recurParams['frequency_unit'] = $params['frequency_unit'];
2519 }
2520
2521 $this->ids['membership_type'] = $this->membershipTypeCreate($membershipParams);
69140e67 2522 //create a contribution so our membership & contribution don't both have id = 1
0dea0c7c 2523 if ($this->callAPISuccess('Contribution', 'getcount', []) === 0) {
7cf5bd55 2524 $this->contributionCreate([
b6b59c64 2525 'contact_id' => $this->_contactID,
2526 'is_test' => 1,
2527 'financial_type_id' => 1,
2528 'invoice_id' => 'abcd',
2529 'trxn_id' => 345,
f03241be 2530 'receive_date' => '2019-07-25 07:34:23',
7cf5bd55 2531 ]);
b6b59c64 2532 }
69140e67 2533
0dea0c7c 2534 $this->ids['membership'] = $this->callAPISuccess('Membership', 'create', [
a9ac877b
EM
2535 'contact_id' => $this->_contactID,
2536 'membership_type_id' => $this->ids['membership_type'],
a9ac877b 2537 'format.only_id' => TRUE,
f03241be 2538 'source' => 'Payment',
0dea0c7c 2539 'skipLineItem' => TRUE,
7cf5bd55 2540 ]);
0dea0c7c 2541 $this->setupRecurringPaymentProcessorTransaction($recurParams, [
2542 'line_items' => [
2543 [
2544 'line_item' => [
2545 [
2546 'entity_table' => 'civicrm_membership',
2547 'entity_id' => $this->ids['membership'],
2548 'label' => 'General',
2549 'qty' => 1,
2550 'unit_price' => 200,
2551 'line_total' => 200,
2552 'financial_type_id' => 1,
2553 'price_field_id' => $this->callAPISuccess('price_field', 'getvalue', [
2554 'return' => 'id',
2555 'label' => 'Membership Amount',
2556 'options' => ['limit' => 1, 'sort' => 'id DESC'],
2557 ]),
2558 'price_field_value_id' => $this->callAPISuccess('price_field_value', 'getvalue', [
2559 'return' => 'id',
2560 'label' => 'General',
2561 'options' => ['limit' => 1, 'sort' => 'id DESC'],
2562 ]),
2563 ],
2564 ],
2565 ],
2566 ],
7cf5bd55 2567 ]);
0dea0c7c 2568 $this->callAPISuccess('Membership', 'create', ['id' => $this->ids['membership'], 'contribution_recur_id' => $this->_contributionRecurID]);
a9ac877b 2569 }
6a488035 2570
4cbe18b8
EM
2571 /**
2572 * @param $message
2573 *
2574 * @throws Exception
a9ac877b 2575 */
00be9182 2576 public function CiviUnitTestCase_fatalErrorHandler($message) {
a9ac877b
EM
2577 throw new Exception("{$message['message']}: {$message['code']}");
2578 }
4aef704e 2579
d67f1f28 2580 /**
eceb18cc 2581 * Wrap the entire test case in a transaction.
d67f1f28
TO
2582 *
2583 * Only subsequent DB statements will be wrapped in TX -- this cannot
2584 * retroactively wrap old DB statements. Therefore, it makes sense to
2585 * call this at the beginning of setUp().
2586 *
2587 * Note: Recall that TRUNCATE and ALTER will force-commit transactions, so
2588 * this option does not work with, e.g., custom-data.
2589 *
2590 * WISHLIST: Monitor SQL queries in unit-tests and generate an exception
2591 * if TRUNCATE or ALTER is called while using a transaction.
2592 *
e16033b4
TO
2593 * @param bool $nest
2594 * Whether to use nesting or reference-counting.
d67f1f28 2595 */
00be9182 2596 public function useTransaction($nest = TRUE) {
d67f1f28
TO
2597 if (!$this->tx) {
2598 $this->tx = new CRM_Core_Transaction($nest);
2599 $this->tx->rollback();
2600 }
2601 }
a335f6b2 2602
b3c30fda 2603 /**
54957108 2604 * Assert the attachment exists.
2605 *
2606 * @param bool $exists
b3c30fda
CW
2607 * @param array $apiResult
2608 */
2609 protected function assertAttachmentExistence($exists, $apiResult) {
2610 $fileId = $apiResult['id'];
2611 $this->assertTrue(is_numeric($fileId));
2612 $this->assertEquals($exists, file_exists($apiResult['values'][$fileId]['path']));
7cf5bd55 2613 $this->assertDBQuery($exists ? 1 : 0, 'SELECT count(*) FROM civicrm_file WHERE id = %1', [
2614 1 => [$fileId, 'Int'],
2615 ]);
2616 $this->assertDBQuery($exists ? 1 : 0, 'SELECT count(*) FROM civicrm_entity_file WHERE id = %1', [
2617 1 => [$fileId, 'Int'],
2618 ]);
b3c30fda
CW
2619 }
2620
299c1530 2621 /**
2622 * Assert 2 sql strings are the same, ignoring double spaces.
2623 *
2624 * @param string $expectedSQL
2625 * @param string $actualSQL
2626 * @param string $message
2627 */
2628 protected function assertLike($expectedSQL, $actualSQL, $message = 'different sql') {
2629 $expected = trim((preg_replace('/[ \r\n\t]+/', ' ', $expectedSQL)));
2630 $actual = trim((preg_replace('/[ \r\n\t]+/', ' ', $actualSQL)));
2631 $this->assertEquals($expected, $actual, $message);
2632 }
2633
c039f658 2634 /**
2635 * Create a price set for an event.
2636 *
2637 * @param int $feeTotal
601c7a24 2638 * @param int $minAmt
f660d2ad 2639 * @param string $type
c039f658 2640 *
2d6f64f4 2641 * @param array $options
2642 *
c039f658 2643 * @return int
2644 * Price Set ID.
74531938 2645 * @throws \CRM_Core_Exception
c039f658 2646 */
2d6f64f4 2647 protected function eventPriceSetCreate($feeTotal, $minAmt = 0, $type = 'Text', $options = [['name' => 'hundy', 'amount' => 100]]) {
c039f658 2648 // creating price set, price field
2649 $paramsSet['title'] = 'Price Set';
2650 $paramsSet['name'] = CRM_Utils_String::titleToVar('Price Set');
2651 $paramsSet['is_active'] = FALSE;
2652 $paramsSet['extends'] = 1;
601c7a24 2653 $paramsSet['min_amount'] = $minAmt;
c039f658 2654
f660d2ad 2655 $priceSet = CRM_Price_BAO_PriceSet::create($paramsSet);
2656 $this->_ids['price_set'] = $priceSet->id;
c039f658 2657
7cf5bd55 2658 $paramsField = [
c039f658 2659 'label' => 'Price Field',
2660 'name' => CRM_Utils_String::titleToVar('Price Field'),
f660d2ad 2661 'html_type' => $type,
c039f658 2662 'price' => $feeTotal,
7cf5bd55 2663 'option_label' => ['1' => 'Price Field'],
2664 'option_value' => ['1' => $feeTotal],
2665 'option_name' => ['1' => $feeTotal],
2666 'option_weight' => ['1' => 1],
2667 'option_amount' => ['1' => 1],
c039f658 2668 'is_display_amounts' => 1,
2669 'weight' => 1,
2670 'options_per_line' => 1,
7cf5bd55 2671 'is_active' => ['1' => 1],
f660d2ad 2672 'price_set_id' => $this->_ids['price_set'],
c039f658 2673 'is_enter_qty' => 1,
8484a5f0 2674 'financial_type_id' => $this->getFinancialTypeId('Event Fee'),
7cf5bd55 2675 ];
f660d2ad 2676 if ($type === 'Radio') {
2d6f64f4 2677 foreach ($options as $index => $option) {
2678 $paramsField['is_enter_qty'] = 0;
2679 $optionID = $index + 2;
2680 $paramsField['option_value'][$optionID] = $paramsField['option_weight'][$optionID] = $paramsField['option_amount'][$optionID] = $option['amount'];
2681 $paramsField['option_label'][$optionID] = $paramsField['option_name'][$optionID] = $option['name'];
2682 }
2683
f660d2ad 2684 }
74531938 2685 $this->callAPISuccess('PriceField', 'create', $paramsField);
7cf5bd55 2686 $fields = $this->callAPISuccess('PriceField', 'get', ['price_set_id' => $this->_ids['price_set']]);
f660d2ad 2687 $this->_ids['price_field'] = array_keys($fields['values']);
7cf5bd55 2688 $fieldValues = $this->callAPISuccess('PriceFieldValue', 'get', ['price_field_id' => $this->_ids['price_field'][0]]);
f660d2ad 2689 $this->_ids['price_field_value'] = array_keys($fieldValues['values']);
c039f658 2690
f660d2ad 2691 return $this->_ids['price_set'];
c039f658 2692 }
2693
481312d9 2694 /**
2695 * Add a profile to a contribution page.
2696 *
2697 * @param string $name
2698 * @param int $contributionPageID
5d6cf648 2699 * @param string $module
481312d9 2700 */
5d6cf648
JM
2701 protected function addProfile($name, $contributionPageID, $module = 'CiviContribute') {
2702 $params = [
481312d9 2703 'uf_group_id' => $name,
5d6cf648 2704 'module' => $module,
481312d9 2705 'entity_table' => 'civicrm_contribution_page',
2706 'entity_id' => $contributionPageID,
2707 'weight' => 1,
5d6cf648
JM
2708 ];
2709 if ($module !== 'CiviContribute') {
2710 $params['module_data'] = [$module => []];
2711 }
2712 $this->callAPISuccess('UFJoin', 'create', $params);
481312d9 2713 }
2714
db62fd2b
PN
2715 /**
2716 * Add participant with contribution
2717 *
2718 * @return array
f3e6da5e 2719 *
2720 * @throws \CRM_Core_Exception
db62fd2b 2721 */
5266bd48 2722 protected function createPartiallyPaidParticipantOrder() {
2723 $orderParams = $this->getParticipantOrderParams();
2724 $orderParams['api.Payment.create'] = ['total_amount' => 150];
f3e6da5e 2725 return $this->callAPISuccess('Order', 'create', $orderParams);
db62fd2b
PN
2726 }
2727
73c0e107
PN
2728 /**
2729 * Create price set
2730 *
2731 * @param string $component
2732 * @param int $componentId
39b959db 2733 * @param array $priceFieldOptions
73c0e107
PN
2734 *
2735 * @return array
2736 */
7cf5bd55 2737 protected function createPriceSet($component = 'contribution_page', $componentId = NULL, $priceFieldOptions = []) {
5c3d600f
PN
2738 $paramsSet['title'] = 'Price Set' . substr(sha1(rand()), 0, 7);
2739 $paramsSet['name'] = CRM_Utils_String::titleToVar($paramsSet['title']);
73c0e107 2740 $paramsSet['is_active'] = TRUE;
faba82fc 2741 $paramsSet['financial_type_id'] = 'Event Fee';
73c0e107
PN
2742 $paramsSet['extends'] = 1;
2743 $priceSet = $this->callAPISuccess('price_set', 'create', $paramsSet);
2744 $priceSetId = $priceSet['id'];
2745 //Checking for priceset added in the table.
2746 $this->assertDBCompareValue('CRM_Price_BAO_PriceSet', $priceSetId, 'title',
2747 'id', $paramsSet['title'], 'Check DB for created priceset'
2748 );
7cf5bd55 2749 $paramsField = array_merge([
73c0e107
PN
2750 'label' => 'Price Field',
2751 'name' => CRM_Utils_String::titleToVar('Price Field'),
2752 'html_type' => 'CheckBox',
7cf5bd55 2753 'option_label' => ['1' => 'Price Field 1', '2' => 'Price Field 2'],
2754 'option_value' => ['1' => 100, '2' => 200],
2755 'option_name' => ['1' => 'Price Field 1', '2' => 'Price Field 2'],
2756 'option_weight' => ['1' => 1, '2' => 2],
2757 'option_amount' => ['1' => 100, '2' => 200],
73c0e107
PN
2758 'is_display_amounts' => 1,
2759 'weight' => 1,
2760 'options_per_line' => 1,
7cf5bd55 2761 'is_active' => ['1' => 1, '2' => 1],
73c0e107
PN
2762 'price_set_id' => $priceSet['id'],
2763 'is_enter_qty' => 1,
8484a5f0 2764 'financial_type_id' => $this->getFinancialTypeId('Event Fee'),
7cf5bd55 2765 ], $priceFieldOptions);
c91b1cc3 2766
73c0e107
PN
2767 $priceField = CRM_Price_BAO_PriceField::create($paramsField);
2768 if ($componentId) {
2769 CRM_Price_BAO_PriceSet::addTo('civicrm_' . $component, $componentId, $priceSetId);
2770 }
7cf5bd55 2771 return $this->callAPISuccess('PriceFieldValue', 'get', ['price_field_id' => $priceField->id]);
73c0e107
PN
2772 }
2773
b80f2ad1
E
2774 /**
2775 * Replace the template with a test-oriented template designed to show all the variables.
2776 *
2777 * @param string $templateName
7a2ee417 2778 * @param string $type
b80f2ad1 2779 */
7a2ee417 2780 protected function swapMessageTemplateForTestTemplate($templateName = 'contribution_online_receipt', $type = 'html') {
2781 $testTemplate = file_get_contents(__DIR__ . '/../../templates/message_templates/' . $templateName . '_' . $type . '.tpl');
b80f2ad1 2782 CRM_Core_DAO::executeQuery(
fa839a68 2783 "UPDATE civicrm_msg_template
2784 SET msg_{$type} = %1
2785 WHERE workflow_name = '{$templateName}'
2786 AND is_default = 1", [1 => [$testTemplate, 'String']]
b80f2ad1
E
2787 );
2788 }
2789
2790 /**
2791 * Reinstate the default template.
2792 *
2793 * @param string $templateName
7a2ee417 2794 * @param string $type
b80f2ad1 2795 */
7a2ee417 2796 protected function revertTemplateToReservedTemplate($templateName = 'contribution_online_receipt', $type = 'html') {
b80f2ad1
E
2797 CRM_Core_DAO::executeQuery(
2798 "UPDATE civicrm_option_group og
2799 LEFT JOIN civicrm_option_value ov ON ov.option_group_id = og.id
2800 LEFT JOIN civicrm_msg_template m ON m.workflow_id = ov.id
2801 LEFT JOIN civicrm_msg_template m2 ON m2.workflow_id = ov.id AND m2.is_reserved = 1
7a2ee417 2802 SET m.msg_{$type} = m2.msg_{$type}
b80f2ad1
E
2803 WHERE og.name = 'msg_tpl_workflow_contribution'
2804 AND ov.name = '{$templateName}'
2805 AND m.is_default = 1"
2806 );
2807 }
2808
8d35246a
EM
2809 /**
2810 * Flush statics relating to financial type.
2811 */
2812 protected function flushFinancialTypeStatics() {
2813 if (isset(\Civi::$statics['CRM_Financial_BAO_FinancialType'])) {
2814 unset(\Civi::$statics['CRM_Financial_BAO_FinancialType']);
2815 }
4954339a
EM
2816 if (isset(\Civi::$statics['CRM_Contribute_PseudoConstant'])) {
2817 unset(\Civi::$statics['CRM_Contribute_PseudoConstant']);
2818 }
8d35246a 2819 CRM_Contribute_PseudoConstant::flush('financialType');
4954339a
EM
2820 CRM_Contribute_PseudoConstant::flush('membershipType');
2821 // Pseudoconstants may be saved to the cache table.
2822 CRM_Core_DAO::executeQuery("TRUNCATE civicrm_cache");
7cf5bd55 2823 CRM_Financial_BAO_FinancialType::$_statusACLFt = [];
8d35246a
EM
2824 CRM_Financial_BAO_FinancialType::$_availableFinancialTypes = NULL;
2825 }
2826
2827 /**
2828 * Set the permissions to the supplied array.
2829 *
2830 * @param array $permissions
2831 */
2832 protected function setPermissions($permissions) {
2833 CRM_Core_Config::singleton()->userPermissionClass->permissions = $permissions;
2834 $this->flushFinancialTypeStatics();
2835 }
2836
bf722049 2837 /**
2838 * @param array $params
2839 * @param $context
2840 */
2841 public function _checkFinancialRecords($params, $context) {
7cf5bd55 2842 $entityParams = [
bf722049 2843 'entity_id' => $params['id'],
2844 'entity_table' => 'civicrm_contribution',
7cf5bd55 2845 ];
2846 $contribution = $this->callAPISuccess('contribution', 'getsingle', ['id' => $params['id']]);
bf722049 2847 $this->assertEquals($contribution['total_amount'] - $contribution['fee_amount'], $contribution['net_amount']);
2848 if ($context == 'pending') {
2849 $trxn = CRM_Financial_BAO_FinancialItem::retrieveEntityFinancialTrxn($entityParams);
2850 $this->assertNull($trxn, 'No Trxn to be created until IPN callback');
2851 return;
2852 }
2853 $trxn = current(CRM_Financial_BAO_FinancialItem::retrieveEntityFinancialTrxn($entityParams));
7cf5bd55 2854 $trxnParams = [
bf722049 2855 'id' => $trxn['financial_trxn_id'],
7cf5bd55 2856 ];
bf722049 2857 if ($context != 'online' && $context != 'payLater') {
7cf5bd55 2858 $compareParams = [
bf722049 2859 'to_financial_account_id' => 6,
2f3d0664 2860 'total_amount' => (float) CRM_Utils_Array::value('total_amount', $params, 100.00),
bf722049 2861 'status_id' => 1,
7cf5bd55 2862 ];
bf722049 2863 }
2864 if ($context == 'feeAmount') {
2865 $compareParams['fee_amount'] = 50;
2866 }
2867 elseif ($context == 'online') {
7cf5bd55 2868 $compareParams = [
bf722049 2869 'to_financial_account_id' => 12,
2f3d0664 2870 'total_amount' => (float) CRM_Utils_Array::value('total_amount', $params, 100.00),
bf722049 2871 'status_id' => 1,
f69a9ac3 2872 'payment_instrument_id' => CRM_Utils_Array::value('payment_instrument_id', $params, 1),
7cf5bd55 2873 ];
bf722049 2874 }
2875 elseif ($context == 'payLater') {
7cf5bd55 2876 $compareParams = [
bf722049 2877 'to_financial_account_id' => 7,
2f3d0664 2878 'total_amount' => (float) CRM_Utils_Array::value('total_amount', $params, 100.00),
bf722049 2879 'status_id' => 2,
7cf5bd55 2880 ];
bf722049 2881 }
2882 $this->assertDBCompareValues('CRM_Financial_DAO_FinancialTrxn', $trxnParams, $compareParams);
7cf5bd55 2883 $entityParams = [
bf722049 2884 'financial_trxn_id' => $trxn['financial_trxn_id'],
2885 'entity_table' => 'civicrm_financial_item',
7cf5bd55 2886 ];
bf722049 2887 $entityTrxn = current(CRM_Financial_BAO_FinancialItem::retrieveEntityFinancialTrxn($entityParams));
7cf5bd55 2888 $fitemParams = [
bf722049 2889 'id' => $entityTrxn['entity_id'],
7cf5bd55 2890 ];
2891 $compareParams = [
2f3d0664 2892 'amount' => (float) CRM_Utils_Array::value('total_amount', $params, 100.00),
bf722049 2893 'status_id' => 1,
1a1a2f4f 2894 'financial_account_id' => CRM_Utils_Array::value('financial_account_id', $params, 1),
7cf5bd55 2895 ];
bf722049 2896 if ($context == 'payLater') {
7cf5bd55 2897 $compareParams = [
2f3d0664 2898 'amount' => (float) CRM_Utils_Array::value('total_amount', $params, 100.00),
bf722049 2899 'status_id' => 3,
1a1a2f4f 2900 'financial_account_id' => CRM_Utils_Array::value('financial_account_id', $params, 1),
7cf5bd55 2901 ];
bf722049 2902 }
2903 $this->assertDBCompareValues('CRM_Financial_DAO_FinancialItem', $fitemParams, $compareParams);
2904 if ($context == 'feeAmount') {
7cf5bd55 2905 $maxParams = [
bf722049 2906 'entity_id' => $params['id'],
2907 'entity_table' => 'civicrm_contribution',
7cf5bd55 2908 ];
bf722049 2909 $maxTrxn = current(CRM_Financial_BAO_FinancialItem::retrieveEntityFinancialTrxn($maxParams, TRUE));
7cf5bd55 2910 $trxnParams = [
bf722049 2911 'id' => $maxTrxn['financial_trxn_id'],
7cf5bd55 2912 ];
2913 $compareParams = [
bf722049 2914 'to_financial_account_id' => 5,
2915 'from_financial_account_id' => 6,
2916 'total_amount' => 50,
2917 'status_id' => 1,
7cf5bd55 2918 ];
bf722049 2919 $trxnId = CRM_Core_BAO_FinancialTrxn::getFinancialTrxnId($params['id'], 'DESC');
2920 $this->assertDBCompareValues('CRM_Financial_DAO_FinancialTrxn', $trxnParams, $compareParams);
7cf5bd55 2921 $fitemParams = [
bf722049 2922 'entity_id' => $trxnId['financialTrxnId'],
2923 'entity_table' => 'civicrm_financial_trxn',
7cf5bd55 2924 ];
2925 $compareParams = [
2f3d0664 2926 'amount' => 50.00,
bf722049 2927 'status_id' => 1,
2928 'financial_account_id' => 5,
7cf5bd55 2929 ];
bf722049 2930 $this->assertDBCompareValues('CRM_Financial_DAO_FinancialItem', $fitemParams, $compareParams);
2931 }
2932 // This checks that empty Sales tax rows are not being created. If for any reason it needs to be removed the
2933 // line should be copied into all the functions that call this function & evaluated there
2934 // Be really careful not to remove or bypass this without ensuring stray rows do not re-appear
2935 // when calling completeTransaction or repeatTransaction.
7cf5bd55 2936 $this->callAPISuccessGetCount('FinancialItem', ['description' => 'Sales Tax', 'amount' => 0], 0);
bf722049 2937 }
2938
8484a5f0 2939 /**
2940 * Return financial type id on basis of name
2941 *
2942 * @param string $name Financial type m/c name
2943 *
2944 * @return int
2945 */
2946 public function getFinancialTypeId($name) {
2947 return CRM_Core_DAO::getFieldValue('CRM_Financial_DAO_FinancialType', $name, 'id', 'name');
2948 }
2949
204beda3 2950 /**
2951 * Cleanup function for contents of $this->ids.
2952 *
2953 * This is a best effort cleanup to use in tear downs etc.
2954 *
2955 * It will not fail if the data has already been removed (some tests may do
2956 * their own cleanup).
2957 */
2958 protected function cleanUpSetUpIDs() {
2959 foreach ($this->setupIDs as $entity => $id) {
2960 try {
7cf5bd55 2961 civicrm_api3($entity, 'delete', ['id' => $id, 'skip_undelete' => 1]);
204beda3 2962 }
2963 catch (CiviCRM_API3_Exception $e) {
2964 // This is a best-effort cleanup function, ignore.
2965 }
2966 }
2967 }
2968
adbc354b
PN
2969 /**
2970 * Create Financial Type.
2971 *
2972 * @param array $params
2973 *
2974 * @return array
2975 */
7cf5bd55 2976 protected function createFinancialType($params = []) {
adbc354b 2977 $params = array_merge($params,
7cf5bd55 2978 [
adbc354b
PN
2979 'name' => 'Financial-Type -' . substr(sha1(rand()), 0, 7),
2980 'is_active' => 1,
7cf5bd55 2981 ]
adbc354b
PN
2982 );
2983 return $this->callAPISuccess('FinancialType', 'create', $params);
2984 }
2985
e1c5a855 2986 /**
2987 * Create Payment Instrument.
2988 *
2989 * @param array $params
2990 * @param string $financialAccountName
2991 *
2992 * @return int
2993 */
7cf5bd55 2994 protected function createPaymentInstrument($params = [], $financialAccountName = 'Donation') {
2995 $params = array_merge([
e1c5a855 2996 'label' => 'Payment Instrument -' . substr(sha1(rand()), 0, 7),
2997 'option_group_id' => 'payment_instrument',
2998 'is_active' => 1,
7cf5bd55 2999 ], $params);
897ff8c5 3000 $newPaymentInstrument = $this->callAPISuccess('OptionValue', 'create', $params)['id'];
e1c5a855 3001
3002 $relationTypeID = key(CRM_Core_PseudoConstant::accountOptionValues('account_relationship', NULL, " AND v.name LIKE 'Asset Account is' "));
3003
3004 $financialAccountParams = [
3005 'entity_table' => 'civicrm_option_value',
897ff8c5 3006 'entity_id' => $newPaymentInstrument,
e1c5a855 3007 'account_relationship' => $relationTypeID,
3008 'financial_account_id' => $this->callAPISuccess('FinancialAccount', 'getValue', ['name' => $financialAccountName, 'return' => 'id']),
3009 ];
3010 CRM_Financial_BAO_FinancialTypeAccount::add($financialAccountParams);
3011
3012 return CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'payment_instrument_id', $params['label']);
3013 }
3014
ec5da26a 3015 /**
3016 * Enable Tax and Invoicing
b086dc3a 3017 *
3018 * @param array $params
3019 *
3020 * @return \Civi\Core\SettingsBag
ec5da26a 3021 */
7cf5bd55 3022 protected function enableTaxAndInvoicing($params = []) {
ec5da26a 3023 // Enable component contribute setting
3024 $contributeSetting = array_merge($params,
7cf5bd55 3025 [
ec5da26a 3026 'invoicing' => 1,
3027 'invoice_prefix' => 'INV_',
ec5da26a 3028 'due_date' => 10,
3029 'due_date_period' => 'days',
3030 'notes' => '',
3031 'is_email_pdf' => 1,
3032 'tax_term' => 'Sales Tax',
3033 'tax_display_settings' => 'Inclusive',
7cf5bd55 3034 ]
ec5da26a 3035 );
3036 return Civi::settings()->set('contribution_invoice_settings', $contributeSetting);
3037 }
3038
9fbf312f 3039 /**
3040 * Enable Tax and Invoicing
7aa78908 3041 *
3042 * @throws \CRM_Core_Exception
9fbf312f 3043 */
7aa78908 3044 protected function disableTaxAndInvoicing() {
3045 $accounts = $this->callAPISuccess('EntityFinancialAccount', 'get', ['account_relationship' => 'Sales Tax Account is'])['values'];
3046 foreach ($accounts as $account) {
3047 $this->callAPISuccess('EntityFinancialAccount', 'delete', ['id' => $account['id']]);
3048 $this->callAPISuccess('FinancialAccount', 'delete', ['id' => $account['financial_account_id']]);
3049 }
3050
f436577a 3051 if (!empty(\Civi::$statics['CRM_Core_PseudoConstant']) && isset(\Civi::$statics['CRM_Core_PseudoConstant']['taxRates'])) {
3052 unset(\Civi::$statics['CRM_Core_PseudoConstant']['taxRates']);
3053 }
7aa78908 3054 return Civi::settings()->set('invoicing', FALSE);
9fbf312f 3055 }
3056
7a1f3919
PN
3057 /**
3058 * Add Sales Tax relation for financial type with financial account.
3059 *
3060 * @param int $financialTypeId
3061 *
3062 * @return obj
3063 */
3064 protected function relationForFinancialTypeWithFinancialAccount($financialTypeId) {
7cf5bd55 3065 $params = [
7a1f3919
PN
3066 'name' => 'Sales tax account ' . substr(sha1(rand()), 0, 4),
3067 'financial_account_type_id' => key(CRM_Core_PseudoConstant::accountOptionValues('financial_account_type', NULL, " AND v.name LIKE 'Liability' ")),
3068 'is_deductible' => 1,
3069 'is_tax' => 1,
3070 'tax_rate' => 10,
3071 'is_active' => 1,
7cf5bd55 3072 ];
7a1f3919 3073 $account = CRM_Financial_BAO_FinancialAccount::add($params);
7cf5bd55 3074 $entityParams = [
7a1f3919 3075 'entity_table' => 'civicrm_financial_type',
7a1f3919 3076 'entity_id' => $financialTypeId,
5b3543ce 3077 'account_relationship' => key(CRM_Core_PseudoConstant::accountOptionValues('account_relationship', NULL, " AND v.name LIKE 'Sales Tax Account is' ")),
7cf5bd55 3078 ];
5b3543ce 3079
f436577a 3080 // set tax rate (as 10) for provided financial type ID to static variable, later used to fetch tax rates of all financial types
3081 \Civi::$statics['CRM_Core_PseudoConstant']['taxRates'][$financialTypeId] = 10;
3082
5b3543ce
JM
3083 //CRM-20313: As per unique index added in civicrm_entity_financial_account table,
3084 // first check if there's any record on basis of unique key (entity_table, account_relationship, entity_id)
3085 $dao = new CRM_Financial_DAO_EntityFinancialAccount();
3086 $dao->copyValues($entityParams);
3087 $dao->find();
3088 if ($dao->fetch()) {
3089 $entityParams['id'] = $dao->id;
3090 }
3091 $entityParams['financial_account_id'] = $account->id;
3092
7a1f3919
PN
3093 return CRM_Financial_BAO_FinancialTypeAccount::add($entityParams);
3094 }
3095
65e172a3 3096 /**
3097 * Create price set with contribution test for test setup.
3098 *
3099 * This could be merged with 4.5 function setup in api_v3_ContributionPageTest::setUpContributionPage
3100 * on parent class at some point (fn is not in 4.4).
3101 *
3102 * @param $entity
3103 * @param array $params
3104 */
7cf5bd55 3105 public function createPriceSetWithPage($entity = NULL, $params = []) {
3106 $membershipTypeID = $this->membershipTypeCreate(['name' => 'Special']);
3107 $contributionPageResult = $this->callAPISuccess('contribution_page', 'create', [
65e172a3 3108 'title' => "Test Contribution Page",
3109 'financial_type_id' => 1,
3110 'currency' => 'NZD',
3111 'goal_amount' => 50,
3112 'is_pay_later' => 1,
3113 'is_monetary' => TRUE,
3114 'is_email_receipt' => FALSE,
7cf5bd55 3115 ]);
3116 $priceSet = $this->callAPISuccess('price_set', 'create', [
65e172a3 3117 'is_quick_config' => 0,
3118 'extends' => 'CiviMember',
3119 'financial_type_id' => 1,
3120 'title' => 'my Page',
7cf5bd55 3121 ]);
65e172a3 3122 $priceSetID = $priceSet['id'];
3123
3124 CRM_Price_BAO_PriceSet::addTo('civicrm_contribution_page', $contributionPageResult['id'], $priceSetID);
7cf5bd55 3125 $priceField = $this->callAPISuccess('price_field', 'create', [
65e172a3 3126 'price_set_id' => $priceSetID,
3127 'label' => 'Goat Breed',
3128 'html_type' => 'Radio',
7cf5bd55 3129 ]);
3130 $priceFieldValue = $this->callAPISuccess('price_field_value', 'create', [
39b959db
SL
3131 'price_set_id' => $priceSetID,
3132 'price_field_id' => $priceField['id'],
3133 'label' => 'Long Haired Goat',
3134 'amount' => 20,
3135 'financial_type_id' => 'Donation',
3136 'membership_type_id' => $membershipTypeID,
3137 'membership_num_terms' => 1,
7cf5bd55 3138 ]);
3139 $this->_ids['price_field_value'] = [$priceFieldValue['id']];
3140 $priceFieldValue = $this->callAPISuccess('price_field_value', 'create', [
39b959db
SL
3141 'price_set_id' => $priceSetID,
3142 'price_field_id' => $priceField['id'],
3143 'label' => 'Shoe-eating Goat',
3144 'amount' => 10,
3145 'financial_type_id' => 'Donation',
3146 'membership_type_id' => $membershipTypeID,
3147 'membership_num_terms' => 2,
7cf5bd55 3148 ]);
65e172a3 3149 $this->_ids['price_field_value'][] = $priceFieldValue['id'];
3150
7cf5bd55 3151 $priceFieldValue = $this->callAPISuccess('price_field_value', 'create', [
39b959db
SL
3152 'price_set_id' => $priceSetID,
3153 'price_field_id' => $priceField['id'],
3154 'label' => 'Shoe-eating Goat',
3155 'amount' => 10,
3156 'financial_type_id' => 'Donation',
7cf5bd55 3157 ]);
65e172a3 3158 $this->_ids['price_field_value']['cont'] = $priceFieldValue['id'];
3159
3160 $this->_ids['price_set'] = $priceSetID;
3161 $this->_ids['contribution_page'] = $contributionPageResult['id'];
7cf5bd55 3162 $this->_ids['price_field'] = [$priceField['id']];
65e172a3 3163
3164 $this->_ids['membership_type'] = $membershipTypeID;
3165 }
3166
3c9d67b0 3167 /**
3168 * Only specified contact returned.
7cf5bd55 3169 *
3c9d67b0 3170 * @implements CRM_Utils_Hook::aclWhereClause
7cf5bd55 3171 *
3c9d67b0 3172 * @param $type
3173 * @param $tables
3174 * @param $whereTables
3175 * @param $contactID
3176 * @param $where
3177 */
3178 public function aclWhereMultipleContacts($type, &$tables, &$whereTables, &$contactID, &$where) {
3179 $where = " contact_a.id IN (" . implode(', ', $this->allowedContacts) . ")";
3180 }
3181
88ebed7c
JP
3182 /**
3183 * @implements CRM_Utils_Hook::selectWhereClause
3184 *
3185 * @param string $entity
3186 * @param array $clauses
3187 */
3188 public function selectWhereClauseHook($entity, &$clauses) {
3189 if ($entity == 'Event') {
3190 $clauses['event_type_id'][] = "IN (2, 3, 4)";
3191 }
3192 }
3193
29a59599
JP
3194 /**
3195 * An implementation of hook_civicrm_post used with all our test cases.
3196 *
3197 * @param $op
3198 * @param string $objectName
3199 * @param int $objectId
3200 * @param $objectRef
3201 */
3202 public function onPost($op, $objectName, $objectId, &$objectRef) {
3203 if ($op == 'create' && $objectName == 'Individual') {
3204 CRM_Core_DAO::executeQuery(
3205 "UPDATE civicrm_contact SET nick_name = 'munged' WHERE id = %1",
7cf5bd55 3206 [
3207 1 => [$objectId, 'Integer'],
3208 ]
29a59599
JP
3209 );
3210 }
3211
3212 if ($op == 'edit' && $objectName == 'Participant') {
7cf5bd55 3213 $params = [
3214 1 => [$objectId, 'Integer'],
3215 ];
29a59599
JP
3216 $query = "UPDATE civicrm_participant SET source = 'Post Hook Update' WHERE id = %1";
3217 CRM_Core_DAO::executeQuery($query, $params);
3218 }
3219 }
3220
f8df7165 3221 /**
3222 * Instantiate form object.
3223 *
3224 * We need to instantiate the form to run preprocess, which means we have to trick it about the request method.
3225 *
3226 * @param string $class
3227 * Name of form class.
3228 *
4715d267 3229 * @param array $formValues
3230 *
3231 * @param string $pageName
3232 *
f8df7165 3233 * @return \CRM_Core_Form
4715d267 3234 * @throws \CRM_Core_Exception
f8df7165 3235 */
4715d267 3236 public function getFormObject($class, $formValues = [], $pageName = '') {
6986e4f1 3237 $_POST = $formValues;
f8df7165 3238 $form = new $class();
3239 $_SERVER['REQUEST_METHOD'] = 'GET';
6986e4f1 3240 switch ($class) {
3241 case 'CRM_Event_Cart_Form_Checkout_Payment':
3242 case 'CRM_Event_Cart_Form_Checkout_ParticipantsAndPrices':
3243 $form->controller = new CRM_Event_Cart_Controller_Checkout();
3244 break;
3245
3246 default:
3247 $form->controller = new CRM_Core_Controller();
3248 }
3249 if (!$pageName) {
3250 $formParts = explode('_', $class);
3251 $pageName = array_pop($formParts);
3252 }
4715d267 3253 $form->controller->setStateMachine(new CRM_Core_StateMachine($form->controller));
3254 $_SESSION['_' . $form->controller->_name . '_container']['values'][$pageName] = $formValues;
f8df7165 3255 return $form;
3256 }
3257
83644f47 3258 /**
3259 * Get possible thousand separators.
3260 *
3261 * @return array
3262 */
3263 public function getThousandSeparators() {
7cf5bd55 3264 return [['.'], [',']];
83644f47 3265 }
3266
ebebd629 3267 /**
3268 * Get the boolean options as a provider.
3269 *
3270 * @return array
3271 */
3272 public function getBooleanDataProvider() {
3273 return [[TRUE], [FALSE]];
3274 }
3275
83644f47 3276 /**
3277 * Set the separators for thousands and decimal points.
3278 *
34e322db 3279 * Note that this only covers some common scenarios.
3280 *
3281 * It does not cater for a situation where the thousand separator is a [space]
3282 * Latter is the Norwegian localization. At least some tests need to
3283 * use setMonetaryDecimalPoint and setMonetaryThousandSeparator directly
3284 * to provide broader coverage.
dff93dda 3285 *
83644f47 3286 * @param string $thousandSeparator
3287 */
3288 protected function setCurrencySeparators($thousandSeparator) {
3289 Civi::settings()->set('monetaryThousandSeparator', $thousandSeparator);
34e322db 3290 Civi::settings()->set('monetaryDecimalPoint', ($thousandSeparator === ',' ? '.' : ','));
83644f47 3291 }
3292
dff93dda
JJ
3293 /**
3294 * Sets the thousand separator.
3295 *
3296 * If you use this function also set the decimal separator: setMonetaryDecimalSeparator
3297 *
3298 * @param $thousandSeparator
3299 */
3300 protected function setMonetaryThousandSeparator($thousandSeparator) {
3301 Civi::settings()->set('monetaryThousandSeparator', $thousandSeparator);
3302 }
3303
3304 /**
3305 * Sets the decimal separator.
3306 *
3307 * If you use this function also set the thousand separator setMonetaryDecimalPoint
3308 *
80da79bb 3309 * @param $decimalPoint
dff93dda
JJ
3310 */
3311 protected function setMonetaryDecimalPoint($decimalPoint) {
3312 Civi::settings()->set('monetaryDecimalPoint', $decimalPoint);
3313 }
3314
3315 /**
3316 * Sets the default currency.
3317 *
3318 * @param $currency
3319 */
3320 protected function setDefaultCurrency($currency) {
3321 Civi::settings()->set('defaultCurrency', $currency);
3322 }
3323
83644f47 3324 /**
3325 * Format money as it would be input.
3326 *
3327 * @param string $amount
3328 *
3329 * @return string
3330 */
3331 protected function formatMoneyInput($amount) {
3332 return CRM_Utils_Money::format($amount, NULL, '%a');
3333 }
3334
3ca4bd1b 3335 /**
3336 * Get the contribution object.
3337 *
3338 * @param int $contributionID
3339 *
3340 * @return \CRM_Contribute_BAO_Contribution
3341 */
3342 protected function getContributionObject($contributionID) {
3343 $contributionObj = new CRM_Contribute_BAO_Contribution();
3344 $contributionObj->id = $contributionID;
3345 $contributionObj->find(TRUE);
3346 return $contributionObj;
3347 }
3348
df3320dc 3349 /**
3350 * Enable multilingual.
3351 */
3352 public function enableMultilingual() {
7cf5bd55 3353 $this->callAPISuccess('Setting', 'create', [
df3320dc 3354 'lcMessages' => 'en_US',
7cf5bd55 3355 'languageLimit' => [
df3320dc 3356 'en_US' => 1,
7cf5bd55 3357 ],
3358 ]);
df3320dc 3359
3360 CRM_Core_I18n_Schema::makeMultilingual('en_US');
3361
3362 global $dbLocale;
3363 $dbLocale = '_en_US';
3364 }
3365
51c566a3
SL
3366 /**
3367 * Setup or clean up SMS tests
7cf5bd55 3368 *
51c566a3
SL
3369 * @param bool $teardown
3370 *
3371 * @throws \CiviCRM_API3_Exception
3372 */
3373 public function setupForSmsTests($teardown = FALSE) {
3374 require_once 'CiviTest/CiviTestSMSProvider.php';
3375
3376 // Option value params for CiviTestSMSProvider
3377 $groupID = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionGroup', 'sms_provider_name', 'id', 'name');
7cf5bd55 3378 $params = [
51c566a3
SL
3379 'option_group_id' => $groupID,
3380 'label' => 'unittestSMS',
3381 'value' => 'unit.test.sms',
7cf5bd55 3382 'name' => 'CiviTestSMSProvider',
51c566a3 3383 'is_default' => 1,
7cf5bd55 3384 'is_active' => 1,
3385 'version' => 3,
3386 ];
51c566a3
SL
3387
3388 if ($teardown) {
3389 // Test completed, delete provider
3390 $providerOptionValueResult = civicrm_api3('option_value', 'get', $params);
7cf5bd55 3391 civicrm_api3('option_value', 'delete', ['id' => $providerOptionValueResult['id']]);
51c566a3
SL
3392 return;
3393 }
3394
3395 // Create an SMS provider "CiviTestSMSProvider". Civi handles "CiviTestSMSProvider" as a special case and allows it to be instantiated
3396 // in CRM/Sms/Provider.php even though it is not an extension.
3397 return civicrm_api3('option_value', 'create', $params);
3398 }
3399
68989e71 3400 /**
3401 * Start capturing browser output.
3402 *
3403 * The starts the process of browser output being captured, setting any variables needed for e-notice prevention.
3404 */
3405 protected function startCapturingOutput() {
3406 ob_start();
3407 $_SERVER['HTTP_USER_AGENT'] = 'unittest';
3408 }
3409
3410 /**
3411 * Stop capturing browser output and return as a csv.
3412 *
3413 * @param bool $isFirstRowHeaders
3414 *
3415 * @return \League\Csv\Reader
3416 *
3417 * @throws \League\Csv\Exception
3418 */
3419 protected function captureOutputToCSV($isFirstRowHeaders = TRUE) {
3420 $output = ob_get_flush();
3421 $stream = fopen('php://memory', 'r+');
3422 fwrite($stream, $output);
3423 rewind($stream);
1fc9f4a7 3424 $this->assertEquals("\xEF\xBB\xBF", substr($output, 0, 3));
68989e71 3425 $csv = Reader::createFromString($output);
3426 if ($isFirstRowHeaders) {
3427 $csv->setHeaderOffset(0);
3428 }
3429 ob_clean();
3430 return $csv;
3431 }
3432
91786f44 3433 /**
3434 * Rename various labels to not match the names.
3435 *
3436 * Doing these mimics the fact the name != the label in international installs & triggers failures in
3437 * code that expects it to.
3438 */
3439 protected function renameLabels() {
3440 $replacements = ['Pending', 'Refunded'];
3441 foreach ($replacements as $name) {
3442 CRM_Core_DAO::executeQuery("UPDATE civicrm_option_value SET label = '{$name} Label**' where label = '{$name}' AND name = '{$name}'");
3443 }
3444 }
3445
3446 /**
3447 * Undo any label renaming.
3448 */
3449 protected function resetLabels() {
3450 CRM_Core_DAO::executeQuery("UPDATE civicrm_option_value SET label = REPLACE(name, ' Label**', '') WHERE label LIKE '% Label**'");
3451 }
3452
5266bd48 3453 /**
3454 * Get parameters to set up a multi-line participant order.
3455 *
3456 * @return array
3457 * @throws \CRM_Core_Exception
3458 */
3459 protected function getParticipantOrderParams(): array {
3460 $this->_contactId = $this->individualCreate();
3461 $event = $this->eventCreate();
3462 $this->_eventId = $event['id'];
3463 $eventParams = [
3464 'id' => $this->_eventId,
3465 'financial_type_id' => 4,
3466 'is_monetary' => 1,
3467 ];
3468 $this->callAPISuccess('event', 'create', $eventParams);
3469 $priceFields = $this->createPriceSet('event', $this->_eventId);
3470 $participantParams = [
3471 'financial_type_id' => 4,
3472 'event_id' => $this->_eventId,
3473 'role_id' => 1,
3474 'status_id' => 14,
3475 'fee_currency' => 'USD',
3476 'contact_id' => $this->_contactId,
3477 ];
3478 $participant = $this->callAPISuccess('Participant', 'create', $participantParams);
3479 $orderParams = [
3480 'total_amount' => 300,
3481 'currency' => 'USD',
3482 'contact_id' => $this->_contactId,
3483 'financial_type_id' => 4,
3484 'contribution_status_id' => 'Pending',
3485 'contribution_mode' => 'participant',
3486 'participant_id' => $participant['id'],
3487 ];
3488 foreach ($priceFields['values'] as $key => $priceField) {
3489 $orderParams['line_items'][] = [
3490 'line_item' => [
3491 [
3492 'price_field_id' => $priceField['price_field_id'],
3493 'price_field_value_id' => $priceField['id'],
3494 'label' => $priceField['label'],
3495 'field_title' => $priceField['label'],
3496 'qty' => 1,
3497 'unit_price' => $priceField['amount'],
3498 'line_total' => $priceField['amount'],
3499 'financial_type_id' => $priceField['financial_type_id'],
3500 'entity_table' => 'civicrm_participant',
3501 ],
3502 ],
3503 'params' => $participant,
3504 ];
3505 }
3506 return $orderParams;
3507 }
3508
3d4f6f65 3509 /**
3510 * @param $payments
3511 *
3512 * @throws \CRM_Core_Exception
3513 */
3514 protected function validatePayments($payments) {
3515 foreach ($payments as $payment) {
4804f442 3516 $balance = CRM_Contribute_BAO_Contribution::getContributionBalance($payment['contribution_id']);
3517 if ($balance < 0 && $balance + $payment['total_amount'] === 0.0) {
3518 // This is an overpayment situation. there are no financial items to allocate the overpayment.
3519 // This is a pretty rough way at guessing which payment is the overpayment - but
3520 // for the test suite it should be enough.
3521 continue;
3522 }
3d4f6f65 3523 $items = $this->callAPISuccess('EntityFinancialTrxn', 'get', [
3524 'financial_trxn_id' => $payment['id'],
3525 'entity_table' => 'civicrm_financial_item',
3526 'return' => ['amount'],
3527 ])['values'];
3528 $itemTotal = 0;
3529 foreach ($items as $item) {
3530 $itemTotal += $item['amount'];
3531 }
3532 $this->assertEquals($payment['total_amount'], $itemTotal);
3533 }
3534 }
3535
3536 /**
3537 * Validate all created payments.
3538 *
3539 * @throws \CRM_Core_Exception
3540 */
3541 protected function validateAllPayments() {
3542 $payments = $this->callAPISuccess('Payment', 'get', ['options' => ['limit' => 0]])['values'];
3543 $this->validatePayments($payments);
3544 }
3545
7aeb7f06 3546 /**
3547 * Validate all created contributions.
3548 *
3549 * @throws \CRM_Core_Exception
3550 */
3551 protected function validateAllContributions() {
3552 $contributions = $this->callAPISuccess('Contribution', 'get', [])['values'];
3553 foreach ($contributions as $contribution) {
3554 $lineItems = $this->callAPISuccess('LineItem', 'get', ['contribution_id' => $contribution['id']])['values'];
3555 $total = 0;
3556 foreach ($lineItems as $lineItem) {
3557 $total += $lineItem['line_total'];
3558 }
3559 $this->assertEquals($total, $contribution['total_amount']);
3560 }
3561 }
3562
d943e78d 3563 /**
62ee752a
JG
3564 * @return array|int
3565 * @throws \CRM_Core_Exception
3566 */
3567 protected function createRuleGroup() {
3568 $ruleGroup = $this->callAPISuccess('RuleGroup', 'create', [
3569 'contact_type' => 'Individual',
3570 'threshold' => 8,
3571 'used' => 'General',
3572 'name' => 'TestRule',
3573 'title' => 'TestRule',
3574 'is_reserved' => 0,
3575 ]);
3576 return $ruleGroup;
3577 }
3578
3579 /**
d943e78d 3580 * Generic create test.
3581 *
3582 * @param int $version
3583 *
3584 * @throws \CRM_Core_Exception
3585 */
3586 protected function basicCreateTest(int $version) {
3587 $this->_apiversion = $version;
3588 $result = $this->callAPIAndDocument($this->_entity, 'create', $this->params, __FUNCTION__, __FILE__);
3589 $this->assertEquals(1, $result['count']);
3590 $this->assertNotNull($result['values'][$result['id']]['id']);
3591 $this->getAndCheck($this->params, $result['id'], $this->_entity);
3592 }
3593
3594 /**
3595 * Generic delete test.
3596 *
3597 * @param int $version
3598 *
3599 * @throws \CRM_Core_Exception
3600 */
3601 protected function basicDeleteTest($version) {
3602 $this->_apiversion = $version;
3603 $result = $this->callAPISuccess($this->_entity, 'create', $this->params);
3604 $deleteParams = ['id' => $result['id']];
3605 $this->callAPIAndDocument($this->_entity, 'delete', $deleteParams, __FUNCTION__, __FILE__);
3606 $checkDeleted = $this->callAPISuccess($this->_entity, 'get', []);
3607 $this->assertEquals(0, $checkDeleted['count']);
3608 }
3609
7580050c 3610 /**
3611 * Create and return a case object for the given Client ID.
3612 *
3613 * @param int $clientId
3614 * @param int $loggedInUser
3615 * Omit or pass NULL to use the same as clientId
3616 * @param array $extra
3617 * Optional specific parameters such as start_date
3618 *
3619 * @return CRM_Case_BAO_Case
3620 */
3621 public function createCase($clientId, $loggedInUser = NULL, $extra = NULL) {
3622 if (empty($loggedInUser)) {
3623 // backwards compatibility - but it's more typical that the creator is a different person than the client
3624 $loggedInUser = $clientId;
3625 }
3626 $caseParams = [
3627 'activity_subject' => 'Case Subject',
3628 'client_id' => $clientId,
3629 'case_type_id' => 1,
3630 'status_id' => 1,
3631 'case_type' => 'housing_support',
3632 'subject' => 'Case Subject',
3633 'start_date' => ($extra['start_date'] ?? date("Y-m-d")),
3634 'start_date_time' => ($extra['start_date_time'] ?? date("YmdHis")),
3635 'medium_id' => 2,
3636 'activity_details' => '',
3637 ];
3638 $form = new CRM_Case_Form_Case();
3639 $caseObj = $form->testSubmit($caseParams, "OpenCase", $loggedInUser, "standalone");
3640 return $caseObj;
3641 }
3642
a86d27fc 3643}