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