86df6ae15a9f3b098b1482f23aaeb6bcc4f2682f
[civicrm-core.git] / tests / phpunit / CRM / Export / BAO / ExportTest.php
1 <?php
2
3 /**
4 * Class CRM_Core_DAOTest
5 *
6 * @group headless
7 */
8 class CRM_Export_BAO_ExportTest extends CiviUnitTestCase {
9
10 use CRMTraits_Custom_CustomDataTrait;
11 /**
12 * Contact IDs created for testing.
13 *
14 * @var array
15 */
16 protected $contactIDs = [];
17
18 /**
19 * Contribution IDs created for testing.
20 *
21 * @var array
22 */
23 protected $contributionIDs = [];
24
25 /**
26 * Contribution IDs created for testing.
27 *
28 * @var array
29 */
30 protected $activityIDs = [];
31
32 /**
33 * Contribution IDs created for testing.
34 *
35 * @var array
36 */
37 protected $membershipIDs = [];
38
39 /**
40 * Master Address ID created for testing.
41 *
42 * @var int
43 */
44 protected $masterAddressID;
45
46 protected $locationTypes = [];
47
48 /**
49 * Processor generated in test.
50 *
51 * @var \CRM_Export_BAO_ExportProcessor
52 */
53 protected $processor;
54
55 /**
56 * Csv output from export.
57 *
58 * @var \League\Csv\Reader
59 */
60 protected $csv;
61
62 /**
63 * Cleanup data.
64 *
65 * @throws \Exception
66 */
67 public function tearDown() {
68 $this->quickCleanUpFinancialEntities();
69 $this->quickCleanup([
70 'civicrm_contact',
71 'civicrm_email',
72 'civicrm_phone',
73 'civicrm_im',
74 'civicrm_website',
75 'civicrm_address',
76 'civicrm_relationship',
77 'civicrm_membership',
78 'civicrm_case',
79 'civicrm_case_contact',
80 'civicrm_case_activity',
81 'civicrm_campaign',
82 ]);
83
84 if (!empty($this->locationTypes)) {
85 $this->callAPISuccess('LocationType', 'delete', ['id' => $this->locationTypes['Whare Kai']['id']]);
86 $this->callAPISuccess('LocationType', 'create', ['id' => $this->locationTypes['Main']['id'], 'name' => 'Main']);
87 }
88 if ($this->processor && $this->processor->getTemporaryTable()) {
89 // delete the export temp table
90 CRM_Core_DAO::executeQuery("DROP TABLE IF EXISTS " . $this->processor->getTemporaryTable());
91 }
92 parent::tearDown();
93 }
94
95 /**
96 * Basic test to ensure the exportComponents function completes without error.
97 *
98 * @throws \CRM_Core_Exception
99 * @throws \League\Csv\Exception
100 */
101 public function testExportComponentsNull() {
102 $this->doExportTest([]);
103 }
104
105 /**
106 * Basic test to ensure the exportComponents function can export selected fields for contribution.
107 *
108 * @throws \CRM_Core_Exception
109 * @throws \League\Csv\Exception
110 */
111 public function testExportComponentsContribution() {
112 $this->setUpContributionExportData();
113 $selectedFields = [
114 ['contact_type' => 'Individual', 'name' => 'first_name'],
115 ['contact_type' => 'Individual', 'name' => 'last_name'],
116 ['name' => 'receive_date'],
117 ['name' => 'contribution_source'],
118 ['contact_type' => 'Individual', 'name' => 'street_address', 1],
119 ['contact_type' => 'Individual', 'name' => 'city', 1],
120 ['contact_type' => 'Individual', 'name' => 'country', 1],
121 ['contact_type' => 'Individual', 'name' => 'email', 1],
122 ['name' => 'trxn_id'],
123 ];
124 $this->hookClass->setHook('civicrm_export', array($this, 'confirmHookWasCalled'));
125
126 $this->doExportTest([
127 'ids' => $this->contributionIDs,
128 'order' => 'receive_date desc',
129 'fields' => $selectedFields,
130 'exportMode' => CRM_Export_Form_Select::CONTRIBUTE_EXPORT,
131 'componentClause' => 'civicrm_contribution.id IN ( ' . implode(',', $this->contributionIDs) . ')',
132 ]);
133 $this->assertContains('display', array_values($this->csv->getHeader()));
134 $row = $this->csv->fetchOne(0);
135 $this->assertEquals('This is a test', $row['display']);
136 }
137
138 /**
139 * Implements hook_civicrm_export().
140 *
141 */
142 public function confirmHookWasCalled(&$exportTempTable, &$headerRows, &$sqlColumns, $exportMode, $componentTable, $ids) {
143 $sqlColumns['display'] = 'display varchar(255)';
144 $headerRows[] = 'display';
145 CRM_Core_DAO::executeQuery("ALTER TABLE $exportTempTable ADD COLUMN display varchar(255)");
146 CRM_Core_DAO::executeQuery("UPDATE $exportTempTable SET display = 'This is a test'");
147 }
148
149 /**
150 * Basic test to ensure the exportComponents function can export with soft credits enabled.
151 *
152 * @throws \CRM_Core_Exception
153 * @throws \League\Csv\Exception
154 */
155 public function testExportComponentsContributionSoftCredits() {
156 $this->setUpContributionExportData();
157 $this->callAPISuccess('ContributionSoft', 'create', ['contact_id' => $this->contactIDs[1], 'contribution_id' => $this->contributionIDs[0], 'amount' => 5]);
158 $params = [
159 ['receive_date_low', '=', '20160101000000', 0, 0],
160 ['receive_date_high', '=', '20191231235959', 0, 0],
161 ['contribution_amount_low', '=', '1', 0, 0],
162 ['contribution_amount_high', '=', '10000000', 0, 0],
163 ['contribution_test', '=', '0', 0, 0],
164 ['contribution_or_softcredits', '=', 'both', 0, 0],
165 ];
166
167 $this->doExportTest([
168 'selectAll' => FALSE,
169 'ids' => $this->contributionIDs,
170 'params' => $params,
171 'order' => 'receive_date desc',
172 'exportMode' => CRM_Export_Form_Select::CONTRIBUTE_EXPORT,
173 'componentClause' => 'civicrm_contribution.id IN ( ' . implode(',', $this->contributionIDs) . ')',
174 ]);
175
176 $this->assertEquals(array_merge($this->getBasicHeaderDefinition(FALSE), $this->getContributeHeaderDefinition()), $this->csv->getHeader());
177 $this->assertCount(3, $this->csv);
178 $row = $this->csv->fetchOne(0);
179 $this->assertEquals(95, $row['Net Amount']);
180 $this->assertEquals('', $row['Soft Credit Amount']);
181 $row = $this->csv->fetchOne(1);
182 $this->assertEquals(95, $row['Net Amount']);
183 $this->assertEquals(5, $row['Soft Credit Amount']);
184 $this->assertEquals('Anderson, Anthony', $row['Soft Credit For']);
185 $this->assertEquals($this->contributionIDs[0], $row['Soft Credit For Contribution ID']);
186
187 // Ideally we would use a randomised temp table name & use generic temp cleanup for cleanup - but
188 // for now just make sure we don't leave a mess.
189 CRM_Core_DAO::executeQuery('DROP TABLE IF EXISTS contribution_search_scredit_combined');
190
191 }
192
193 /**
194 * Basic test to ensure the exportComponents function can export selected fields for contribution.
195 *
196 * @throws \CRM_Core_Exception
197 * @throws \League\Csv\Exception
198 */
199 public function testExportComponentsMembership() {
200 $this->setUpMembershipExportData();
201 $this->doExportTest([
202 'selectAll' => TRUE,
203 'ids' => $this->membershipIDs,
204 'exportMode' => CRM_Export_Form_Select::MEMBER_EXPORT,
205 'componentClause' => 'civicrm_membership.id IN ( ' . $this->ids['membership'] . ')',
206 ]);
207 $membership = $this->callAPISuccessGetSingle('Membership', ['id' => $this->ids['membership']]);
208
209 $row = $this->csv->fetchOne();
210 $expected = [
211 'Contact ID' => $this->contactIDs[0],
212 'Contact Type' => 'Individual',
213 'Contact Subtype' => '',
214 'Do Not Email' => '',
215 'Do Not Phone' => '',
216 'Do Not Mail' => '',
217 'Do Not Sms' => '',
218 'Do Not Trade' => '',
219 'No Bulk Emails (User Opt Out)' => '',
220 'Legal Identifier' => '',
221 'External Identifier' => '',
222 'Sort Name' => 'Anderson, Anthony',
223 'Display Name' => 'Mr. Anthony Anderson II',
224 'Nickname' => '',
225 'Legal Name' => '',
226 'Image Url' => '',
227 'Preferred Communication Method' => '',
228 'Preferred Language' => 'en_US',
229 'Preferred Mail Format' => 'Both',
230 'Contact Hash' => '059023a02d27d4e7f285a40ee0e30be8',
231 'Contact Source' => '',
232 'First Name' => 'Anthony',
233 'Middle Name' => 'J.',
234 'Last Name' => 'Anderson',
235 'Individual Prefix' => 'Mr.',
236 'Individual Suffix' => 'II',
237 'Formal Title' => '',
238 'Communication Style' => 'Formal',
239 'Email Greeting ID' => '1',
240 'Postal Greeting ID' => '1',
241 'Addressee ID' => '1',
242 'Job Title' => '',
243 'Gender' => 'Female',
244 'Birth Date' => '',
245 'Deceased' => '',
246 'Deceased Date' => '',
247 'Household Name' => '',
248 'Organization Name' => '',
249 'Sic Code' => '',
250 'Unique ID (OpenID)' => '',
251 'Current Employer ID' => '',
252 'Contact is in Trash' => '',
253 'Created Date' => '2019-07-11 09:56:18',
254 'Modified Date' => '2019-07-11 09:56:19',
255 'Addressee' => 'Mr. Anthony J. Anderson II',
256 'Email Greeting' => 'Dear Anthony',
257 'Postal Greeting' => 'Dear Anthony',
258 'Current Employer' => '',
259 'Location Type' => 'Home',
260 'Street Address' => 'Ambachtstraat 23',
261 'Street Number' => '',
262 'Street Number Suffix' => '',
263 'Street Name' => '',
264 'Street Unit' => '',
265 'Supplemental Address 1' => '',
266 'Supplemental Address 2' => '',
267 'Supplemental Address 3' => '',
268 'City' => 'Brummen',
269 'Postal Code Suffix' => '',
270 'Postal Code' => '6971 BN',
271 'Latitude' => '',
272 'Longitude' => '',
273 'Address Name' => '',
274 'Master Address Belongs To' => '',
275 'County' => '',
276 'State' => '',
277 'Country' => 'Netherlands',
278 'Phone' => '',
279 'Phone Extension' => '',
280 'Phone Type' => '',
281 'Email' => 'home@example.com',
282 'On Hold' => '',
283 'Use for Bulk Mail' => '',
284 'Signature Text' => '',
285 'Signature Html' => '',
286 'IM Provider' => '',
287 'IM Screen Name' => '',
288 'OpenID' => '',
289 'World Region' => 'Europe and Central Asia',
290 'Website' => '',
291 'Membership Type' => 'General',
292 'Test' => '',
293 'Is Pay Later' => '',
294 'Member Since' => $membership['join_date'],
295 'Membership Start Date' => $membership['start_date'],
296 'Membership Expiration Date' => $membership['end_date'],
297 'Source' => 'Payment',
298 'Membership Status' => 'New',
299 'Membership ID' => '2',
300 'Primary Member ID' => '',
301 'Max Related' => '',
302 'Membership Recurring Contribution' => 1,
303 'Campaign ID' => '',
304 'Status Override' => '',
305 'Total Amount' => '200.00',
306 'Contribution Status' => 'Pending Label**',
307 'Date Received' => '2019-07-25 07:34:23',
308 'Payment Method' => 'Check',
309 'Transaction ID' => '',
310 ];
311 $this->assertExpectedOutput($expected, $row);
312 }
313
314 /**
315 * Basic test to ensure the exportComponents function can export selected fields for activity
316 *
317 * @throws \CRM_Core_Exception
318 * @throws \League\Csv\Exception
319 */
320 public function testExportComponentsActivity() {
321 $this->setUpActivityExportData();
322 $selectedFields = [
323 ['contact_type' => 'Individual', 'name' => 'display_name'],
324 ['contact_type' => 'Individual', 'relationship_type_id' => '5', 'relationship_direction' => 'a_b', 'name' => 'display_name'],
325 ];
326
327 $this->doExportTest([
328 'ids' => $this->activityIDs,
329 'order' => '`activity_date_time` desc',
330 'fields' => $selectedFields,
331 'exportMode' => CRM_Export_Form_Select::ACTIVITY_EXPORT,
332 'componentClause' => 'civicrm_activity.id IN ( ' . implode(',', $this->activityIDs) . ')',
333 ]);
334 $row = $this->csv->fetchOne();
335 $this->assertEquals($this->activityIDs[0], $row['Activity ID']);
336 }
337
338 /**
339 * Test the function that extracts the arrays used to structure the output.
340 *
341 * The keys in the output fields array should by matched by field aliases in the sql query (with
342 * exceptions of course - currently country is one - although maybe a future refactor can change that!).
343 *
344 * We are trying to move towards simpler processing in the per row iteration as that may be
345 * repeated 100,000 times and in general we should simply be able to match the query fields to
346 * our expected rows & do a little pseudoconstant mapping.
347 */
348 public function testGetExportStructureArrays() {
349 // This is how return properties are formatted internally within the function for passing to the BAO query.
350 $returnProperties = [
351 'first_name' => 1,
352 'last_name' => 1,
353 'receive_date' => 1,
354 'contribution_source' => 1,
355 'location' => [
356 'Home' => [
357 'street_address' => 1,
358 'city' => 1,
359 'country' => 1,
360 'email' => 1,
361 'im-1' => 1,
362 'im_provider' => 1,
363 'phone-1' => 1,
364 ],
365 ],
366 'phone' => 1,
367 'trxn_id' => 1,
368 'contribution_id' => 1,
369 ];
370
371 $query = new CRM_Contact_BAO_Query([], $returnProperties, NULL,
372 FALSE, FALSE, CRM_Contact_BAO_Query::MODE_CONTRIBUTE,
373 FALSE, TRUE, TRUE, NULL, 'AND'
374 );
375
376 list($select) = $query->query();
377 $pattern = '/as `?([^`,]*)/';
378 $queryFieldAliases = [];
379 preg_match_all($pattern, $select, $queryFieldAliases, PREG_PATTERN_ORDER);
380 $processor = new CRM_Export_BAO_ExportProcessor(CRM_Contact_BAO_Query::MODE_CONTRIBUTE, NULL, 'AND');
381 $processor->setQueryFields($query->_fields);
382 $processor->setReturnProperties($returnProperties);
383
384 list($outputFields) = $processor->getExportStructureArrays();
385 foreach (array_keys($outputFields) as $fieldAlias) {
386 if ($fieldAlias === 'Home-country') {
387 $this->assertTrue(in_array($fieldAlias . '_id', $queryFieldAliases[1]), 'Country is subject to some funky translate so we make sure country id is present');
388 }
389 else {
390 $this->assertTrue(in_array($fieldAlias, $queryFieldAliases[1]), 'looking for field ' . $fieldAlias . ' in generaly the alias fields need to match the outputfields');
391 }
392 }
393
394 }
395
396 /**
397 * Set up some data for us to do testing on.
398 *
399 * @throws \CRM_Core_Exception
400 */
401 public function setUpContributionExportData() {
402 $this->setUpContactExportData();
403 $this->contributionIDs[] = $this->contributionCreate(['contact_id' => $this->contactIDs[0], 'trxn_id' => 'null', 'invoice_id' => 'null', 'receive_date' => '2019-07-25 07:34:23']);
404 $this->contributionIDs[] = $this->contributionCreate(['contact_id' => $this->contactIDs[1], 'trxn_id' => 'null', 'invoice_id' => 'null', 'receive_date' => '2018-12-01 00:00:00']);
405 }
406
407 /**
408 * Set up some data for us to do testing on.
409 *
410 * @throws \CRM_Core_Exception
411 */
412 public function setUpMembershipExportData() {
413 $this->setUpContactExportData();
414 // Create an extra so we don't get false passes due to 1
415 $this->contactMembershipCreate(['contact_id' => $this->contactIDs[0]]);
416
417 $this->_contactID = $this->contactIDs[0];
418 $this->_invoiceID = 1234;
419 $this->_contributionPageID = NULL;
420 $this->_paymentProcessorID = $this->paymentProcessorCreate();
421 $this->setupMembershipRecurringPaymentProcessorTransaction();
422
423 $membershipID = $this->callAPISuccessGetValue('Membership', ['return' => 'id', 'contact_id' => $this->_contactID, 'options' => ['limit' => 1, 'sort' => 'id DESC']]);
424
425 $this->membershipIDs[] = $membershipID;
426 }
427
428 /**
429 * Set up data to test case export.
430 *
431 * @throws \CRM_Core_Exception
432 */
433 public function setupCaseExportData() {
434 $contactID1 = $this->individualCreate();
435 $contactID2 = $this->individualCreate([], 1);
436
437 $case = $this->callAPISuccess('case', 'create', [
438 'case_type_id' => 1,
439 'subject' => 'blah',
440 'contact_id' => $contactID1,
441 ]);
442 $this->callAPISuccess('CaseContact', 'create', [
443 'case_id' => $case['id'],
444 'contact_id' => $contactID2,
445 ]);
446 }
447
448 /**
449 * Set up some data for us to do testing on.
450 *
451 * @throws \CRM_Core_Exception
452 */
453 public function setUpActivityExportData() {
454 $this->setUpContactExportData();
455 $this->activityIDs[] = $this->activityCreate(['contact_id' => $this->contactIDs[0]])['id'];
456 }
457
458 /**
459 * Set up some data for us to do testing on.
460 *
461 * @throws \CRM_Core_Exception
462 */
463 public function setUpContactExportData() {
464 $this->contactIDs[] = $contactA = $this->individualCreate(['gender_id' => 'Female']);
465 // Create address for contact A.
466 $params = [
467 'contact_id' => $contactA,
468 'location_type_id' => 'Home',
469 'street_address' => 'Ambachtstraat 23',
470 'postal_code' => '6971 BN',
471 'country_id' => '1152',
472 'city' => 'Brummen',
473 'is_primary' => 1,
474 ];
475 $result = $this->callAPISuccess('address', 'create', $params);
476 $addressId = $result['id'];
477
478 $this->callAPISuccess('email', 'create', [
479 'id' => $this->callAPISuccessGetValue('Email', ['contact_id' => $params['contact_id'], 'return' => 'id']),
480 'location_type_id' => 'Home',
481 'email' => 'home@example.com',
482 'is_primary' => 1,
483 ]);
484 $this->callAPISuccess('email', 'create', ['contact_id' => $params['contact_id'], 'location_type_id' => 'Work', 'email' => 'work@example.com', 'is_primary' => 0]);
485
486 $params['is_primary'] = 0;
487 $params['location_type_id'] = 'Work';
488 $this->callAPISuccess('address', 'create', $params);
489 $this->contactIDs[] = $contactB = $this->individualCreate();
490
491 $this->callAPISuccess('address', 'create', [
492 'contact_id' => $contactB,
493 'location_type_id' => "Home",
494 'master_id' => $addressId,
495 ]);
496 $this->masterAddressID = $addressId;
497
498 }
499
500 /**
501 * Test variants of primary address exporting.
502 *
503 * @param int $isPrimaryOnly
504 *
505 * @dataProvider getBooleanDataProvider
506 * @throws \CRM_Core_Exception
507 * @throws \League\Csv\Exception
508 */
509 public function testExportPrimaryAddress($isPrimaryOnly) {
510 \Civi::settings()->set('searchPrimaryDetailsOnly', $isPrimaryOnly);
511 $this->setUpContactExportData();
512
513 $selectedFields = [
514 ['contact_type' => 'Individual', 'name' => 'email'],
515 ['contact_type' => 'Individual', 'name' => 'email', 'location_type_id' => '1'],
516 ['contact_type' => 'Individual', 'name' => 'email', 'location_type_id' => '2'],
517 ];
518 $this->doExportTest([
519 'ids' => [],
520 'params' => [['email', 'LIKE', 'c', 0, 1]],
521 'fields' => $selectedFields,
522 'componentClause' => "contact_a.id IN ({$this->contactIDs[0]}, {$this->contactIDs[1]})",
523 'selectAll' => TRUE,
524 ]);
525
526 $row = $this->csv->fetchOne();
527 $this->assertEquals([
528 'Email' => 'home@example.com',
529 'Home-Email' => 'home@example.com',
530 'Work-Email' => 'work@example.com',
531 ], $row);
532 $this->assertEquals(2, count($this->csv));
533 \Civi::settings()->set('searchPrimaryDetailsOnly', FALSE);
534 }
535
536 /**
537 * Test that when exporting a pseudoField it is reset for NULL entries.
538 *
539 * ie. we have a contact WITH a gender & one without - make sure the latter one
540 * does NOT retain the gender of the former.
541 *
542 * @throws \CRM_Core_Exception
543 * @throws \League\Csv\Exception
544 */
545 public function testExportPseudoField() {
546 $this->setUpContactExportData();
547 $this->callAPISuccess('OptionValue', 'create', ['option_group_id' => 'gender', 'name' => 'Really long string', 'value' => 678, 'label' => 'Really long string']);
548 $selectedFields = [['contact_type' => 'Individual', 'name' => 'gender_id']];
549 $this->callAPISuccess('Contact', 'create', ['id' => $this->contactIDs[0], 'gender_id' => 678]);
550 $this->doExportTest(['fields' => $selectedFields, 'ids' => $this->contactIDs]);
551 $row = $this->csv->fetchOne();
552 $this->assertEquals('Really long string', $row['Gender']);
553 }
554
555 /**
556 * Test that when exporting a pseudoField it is reset for NULL entries.
557 *
558 * This is specific to the example in CRM-14398
559 *
560 * @throws \CRM_Core_Exception
561 * @throws \League\Csv\Exception
562 */
563 public function testExportPseudoFieldCampaign() {
564 $this->setUpContributionExportData();
565 $campaign = $this->callAPISuccess('Campaign', 'create', ['title' => 'Big campaign and kinda long too']);
566 $this->callAPISuccess('Contribution', 'create', ['campaign_id' => 'Big_campaign_and_kinda_long_too', 'id' => $this->contributionIDs[0]]);
567 $selectedFields = [
568 ['contact_type' => 'Individual', 'name' => 'gender_id'],
569 ['contact_type' => 'Contribution', 'name' => 'contribution_campaign_title'],
570 ['contact_type' => 'Contribution', 'name' => 'contribution_campaign_id'],
571 ];
572 $this->doExportTest([
573 'ids' => [$this->contactIDs[1]],
574 'exportMode' => CRM_Export_Form_Select::CONTRIBUTE_EXPORT,
575 'fields' => $selectedFields,
576 'componentClause' => "contact_a.id IN (" . implode(",", $this->contactIDs) . ")",
577 ]);
578 $row = $this->csv->fetchOne();
579 $this->assertEquals('Big campaign and kinda long too', $row['Campaign Title']);
580 $this->assertEquals($campaign['id'], $row['Campaign ID']);
581 }
582
583 /**
584 * Test exporting relationships.
585 *
586 * @throws \CRM_Core_Exception
587 * @throws \League\Csv\Exception
588 */
589 public function testExportRelationships() {
590 $organization1 = $this->organizationCreate(['organization_name' => 'Org 1', 'legal_name' => 'pretty legal', 'contact_source' => 'friend who took a law paper once']);
591 $organization2 = $this->organizationCreate(['organization_name' => 'Org 2', 'legal_name' => 'well dodgey']);
592 $contact1 = $this->individualCreate(['employer_id' => $organization1, 'first_name' => 'one']);
593 $contact2 = $this->individualCreate(['employer_id' => $organization2, 'first_name' => 'one']);
594 $employerRelationshipTypeID = $this->callAPISuccessGetValue('RelationshipType', ['return' => 'id', 'label_a_b' => 'Employee of']);
595 $selectedFields = [
596 ['contact_type' => 'Individual', 'name' => 'first_name'],
597 ['contact_type' => 'Individual', 'relationship_type_id' => $employerRelationshipTypeID, 'relationship_direction' => 'a_b', 'name' => 'organization_name'],
598 ['contact_type' => 'Individual', 'relationship_type_id' => $employerRelationshipTypeID, 'relationship_direction' => 'a_b', 'name' => 'legal_name'],
599 ['contact_type' => 'Individual', 'relationship_type_id' => $employerRelationshipTypeID, 'relationship_direction' => 'a_b', 'name' => 'contact_source'],
600 ];
601 $this->doExportTest([
602 'ids' => [$contact1, $contact2],
603 'componentClause' => "contact_a.id IN ( $contact1, $contact2 )",
604 'fields' => $selectedFields,
605 ]);
606
607 $row = $this->csv->fetchOne();
608 $this->assertEquals('one', $row['First Name']);
609 $this->assertEquals('Org 1', $row['Employee of-Organization Name']);
610 $this->assertEquals('pretty legal', $row['Employee of-Legal Name']);
611 $this->assertEquals('friend who took a law paper once', $row['Employee of-Contact Source']);
612
613 $row = $this->csv->fetchOne(1);
614 $this->assertEquals('Org 2', $row['Employee of-Organization Name']);
615 $this->assertEquals('well dodgey', $row['Employee of-Legal Name']);
616 }
617
618 /**
619 * Test exporting relationships.
620 *
621 * This is to ensure that CRM-13995 remains fixed.
622 *
623 * @dataProvider getBooleanDataProvider
624 *
625 * @param bool $includeHouseHold
626 *
627 * @throws CRM_Core_Exception
628 * @throws \League\Csv\Exception
629 */
630 public function testExportRelationshipsMergeToHousehold($includeHouseHold) {
631 list($householdID, $houseHoldTypeID) = $this->setUpHousehold();
632
633 if ($includeHouseHold) {
634 $this->contactIDs[] = $householdID;
635 }
636 $selectedFields = [
637 ['contact_type' => 'Individual', 'relationship_type_id' => $houseHoldTypeID, 'relationship_direction' => 'a_b', 'name' => 'state_province', 'location_type_id' => ''],
638 ['contact_type' => 'Individual', 'relationship_type_id' => $houseHoldTypeID, 'relationship_direction' => 'a_b', 'name' => 'city', 'location_type_id' => ''],
639 ['contact_type' => 'Individual', 'name' => 'city', 'location_type_id' => ''],
640 ['contact_type' => 'Individual', 'name' => 'state_province', 'location_type_id' => ''],
641 ['contact_type' => 'Individual', 'name' => 'contact_source', 'location_type_id' => ''],
642 ];
643 $this->doExportTest([
644 'ids' => $this->contactIDs,
645 'fields' => $selectedFields,
646 'mergeSameHousehold' => TRUE,
647 ]);
648 $row = $this->csv->fetchOne();
649 $this->assertEquals(1, count($this->csv));
650 $this->assertEquals('Portland', $row['City']);
651 $this->assertEquals('ME', $row['State']);
652 $this->assertEquals($householdID, $row['Household ID']);
653 $this->assertEquals('household sauce', $row['Contact Source']);
654 }
655
656 /**
657 * Test exporting relationships.
658 *
659 * @throws \CRM_Core_Exception
660 * @throws \League\Csv\Exception
661 */
662 public function testExportRelationshipsMergeToHouseholdAllFields() {
663 list($householdID) = $this->setUpHousehold();
664 $this->doExportTest(['ids' => $this->contactIDs, 'mergeSameHousehold' => TRUE]);
665 $row = $this->csv->fetchOne();
666 $this->assertCount(1, $this->csv);
667 $this->assertEquals('Unit Test household', $row['Display Name']);
668 $this->assertEquals('Portland', $row['City']);
669 $this->assertEquals('ME', $row['State']);
670 $this->assertEquals($householdID, $row['Household ID']);
671 $this->assertEquals('Unit Test household', $row['Addressee']);
672 $this->assertEquals('Dear Unit Test household', $row['Postal Greeting']);
673 }
674
675 /**
676 * Test custom data exporting.
677 *
678 * @throws \CRM_Core_Exception
679 * @throws \League\Csv\Exception
680 */
681 public function testExportCustomData() {
682 $this->setUpContactExportData();
683 $this->entity = 'Contact';
684 $this->createCustomGroupWithFieldsOfAllTypes();
685 $longString = 'Blah';
686 for ($i = 0; $i < 70; $i++) {
687 $longString .= 'Blah';
688 }
689
690 $this->callAPISuccess('Contact', 'create', [
691 'id' => $this->contactIDs[1],
692 $this->getCustomFieldName('text') => $longString,
693 $this->getCustomFieldName('country') => 'LA',
694 'api.Address.create' => ['location_type_id' => 'Billing', 'city' => 'Waipu'],
695 ]);
696 $selectedFields = [
697 ['name' => 'city', 'location_type_id' => CRM_Core_PseudoConstant::getKey('CRM_Core_BAO_Address', 'location_type_id', 'Billing')],
698 ['name' => $this->getCustomFieldName('text')],
699 ['name' => $this->getCustomFieldName('country')],
700 ];
701
702 $this->doExportTest([
703 'fields' => $selectedFields,
704 'ids' => [$this->contactIDs[1]],
705 ]);
706 $row = $this->csv->fetchOne();
707 $this->assertEquals($longString, $row['Enter text here']);
708 $this->assertEquals('Waipu', $row['Billing-City']);
709 $this->assertEquals("Lao People's Democratic Republic", $row['Country']);
710 }
711
712 /**
713 * Attempt to do a fairly full export of location data.
714 *
715 * @throws \CRM_Core_Exception
716 * @throws \League\Csv\Exception
717 */
718 public function testExportIMData() {
719 // Use default providers.
720 $providers = ['AIM', 'GTalk', 'Jabber', 'MSN', 'Skype', 'Yahoo'];
721 // Main sure labels are not all anglo chars.
722 $this->diversifyLocationTypes();
723
724 $locationTypes = ['Billing' => 'Billing', 'Home' => 'Home', 'Main' => 'Méin', 'Other' => 'Other', 'Whare Kai' => 'Whare Kai'];
725
726 $this->contactIDs[] = $this->individualCreate();
727 $this->contactIDs[] = $this->individualCreate();
728 $this->contactIDs[] = $this->householdCreate();
729 $this->contactIDs[] = $this->organizationCreate();
730 foreach ($this->contactIDs as $contactID) {
731 foreach ($providers as $provider) {
732 foreach ($locationTypes as $locationName => $locationLabel) {
733 $this->callAPISuccess('IM', 'create', [
734 'contact_id' => $contactID,
735 'location_type_id' => $locationName,
736 'provider_id' => $provider,
737 'name' => $locationName . $provider . $contactID,
738 ]);
739 }
740 }
741 }
742
743 $relationships = [
744 $this->contactIDs[1] => ['label' => 'Spouse of'],
745 $this->contactIDs[2] => ['label' => 'Household Member of'],
746 $this->contactIDs[3] => ['label' => 'Employee of'],
747 ];
748
749 foreach ($relationships as $contactID => $relationshipType) {
750 $relationshipTypeID = $this->callAPISuccess('RelationshipType', 'getvalue', ['label_a_b' => $relationshipType['label'], 'return' => 'id']);
751 $result = $this->callAPISuccess('Relationship', 'create', [
752 'contact_id_a' => $this->contactIDs[0],
753 'relationship_type_id' => $relationshipTypeID,
754 'contact_id_b' => $contactID,
755 ]);
756 $relationships[$contactID]['id'] = $result['id'];
757 $relationships[$contactID]['relationship_type_id'] = $relationshipTypeID;
758 }
759
760 $fields = [['Individual', 'contact_id']];
761 // ' ' denotes primary location type.
762 foreach (array_keys(array_merge($locationTypes, [' ' => ['Primary']])) as $locationType) {
763 $fields[] = [
764 'Individual',
765 'im_provider',
766 CRM_Core_PseudoConstant::getKey('CRM_Core_BAO_IM', 'location_type_id', $locationType),
767 ];
768 foreach ($relationships as $contactID => $relationship) {
769 $fields[] = [
770 'Individual',
771 $relationship['relationship_type_id'] . '_a_b',
772 'im_provider',
773 CRM_Core_PseudoConstant::getKey('CRM_Core_BAO_IM', 'location_type_id', $locationType),
774 ];
775 }
776 foreach ($providers as $provider) {
777 $fields[] = [
778 'Individual',
779 'im',
780 CRM_Core_PseudoConstant::getKey('CRM_Core_BAO_IM', 'location_type_id', $locationType),
781 CRM_Core_PseudoConstant::getKey('CRM_Core_BAO_IM', 'provider_id', $provider),
782 ];
783 foreach ($relationships as $contactID => $relationship) {
784 $fields[] = [
785 'Individual',
786 $relationship['relationship_type_id'] . '_a_b',
787 'im',
788 CRM_Core_PseudoConstant::getKey('CRM_Core_BAO_IM', 'location_type_id', $locationType),
789 CRM_Core_PseudoConstant::getKey('CRM_Core_BAO_IM', 'provider_id', $provider),
790 ];
791 }
792 }
793 }
794
795 // @todo switch to just declaring the new format....
796 $mappedFields = [];
797 foreach ($fields as $field) {
798 $mappedFields[] = CRM_Core_BAO_Mapping::getMappingParams([], $field);
799 }
800 $this->doExportTest(['fields' => $mappedFields, 'ids' => [$this->contactIDs[0]]]);
801
802 foreach ($this->csv->getRecords() as $row) {
803 $id = $row['Contact ID'];
804 $this->assertEquals('AIM', $row['Billing-IM Provider']);
805 $this->assertEquals('AIM', $row['Whare Kai-IM Provider']);
806 $this->assertEquals('BillingJabber' . $id, $row['Billing-IM Screen Name-Jabber']);
807 $this->assertEquals('Whare KaiJabber' . $id, $row['Whare Kai-IM Screen Name-Jabber']);
808 $this->assertEquals('BillingSkype' . $id, $row['Billing-IM Screen Name-Skype']);
809 foreach ($relationships as $relatedContactID => $relationship) {
810 $this->assertEquals('BillingYahoo' . $relatedContactID, $row[$relationship['label'] . '-Billing-IM Screen Name-Yahoo']);
811 $this->assertEquals('Whare KaiJabber' . $relatedContactID, $row[$relationship['label'] . '-Whare Kai-IM Screen Name-Jabber']);
812 }
813 }
814 }
815
816 /**
817 * Test phone data export.
818 *
819 * Less over the top complete than the im test.
820 *
821 * @throws \CRM_Core_Exception
822 * @throws \League\Csv\Exception
823 */
824 public function testExportPhoneData() {
825 $this->contactIDs[] = $this->individualCreate();
826 $this->contactIDs[] = $this->individualCreate();
827 $locationTypes = ['Billing' => 'Billing', 'Home' => 'Home'];
828 $phoneTypes = ['Mobile', 'Phone'];
829 foreach ($this->contactIDs as $contactID) {
830 $this->callAPISuccess('Phone', 'create', [
831 'contact_id' => $contactID,
832 'location_type_id' => 'Billing',
833 'phone_type_id' => 'Mobile',
834 'phone' => 'Billing' . 'Mobile' . $contactID,
835 'is_primary' => 1,
836 ]);
837 $this->callAPISuccess('Phone', 'create', [
838 'contact_id' => $contactID,
839 'location_type_id' => 'Home',
840 'phone_type_id' => 'Phone',
841 'phone' => 'Home' . 'Phone' . $contactID,
842 ]);
843 }
844
845 $relationships = [
846 $this->contactIDs[1] => ['label' => 'Spouse of'],
847 ];
848
849 foreach ($relationships as $contactID => $relationshipType) {
850 $relationshipTypeID = $this->callAPISuccess('RelationshipType', 'getvalue', ['label_a_b' => $relationshipType['label'], 'return' => 'id']);
851 $result = $this->callAPISuccess('Relationship', 'create', [
852 'contact_id_a' => $this->contactIDs[0],
853 'relationship_type_id' => $relationshipTypeID,
854 'contact_id_b' => $contactID,
855 ]);
856 $relationships[$contactID]['id'] = $result['id'];
857 $relationships[$contactID]['relationship_type_id'] = $relationshipTypeID;
858 }
859
860 $fields = [['Individual', 'contact_id']];
861 // ' ' denotes primary location type.
862 foreach (array_keys(array_merge($locationTypes, [' ' => ['Primary']])) as $locationType) {
863 $fields[] = [
864 'Individual',
865 'phone',
866 CRM_Core_PseudoConstant::getKey('CRM_Core_BAO_Phone', 'location_type_id', $locationType),
867 ];
868 $fields[] = [
869 'Individual',
870 'phone_type_id',
871 CRM_Core_PseudoConstant::getKey('CRM_Core_BAO_Phone', 'location_type_id', $locationType),
872 ];
873 foreach ($relationships as $contactID => $relationship) {
874 $fields[] = [
875 'Individual',
876 $relationship['relationship_type_id'] . '_a_b',
877 'phone_type_id',
878 CRM_Core_PseudoConstant::getKey('CRM_Core_BAO_Phone', 'location_type_id', $locationType),
879 ];
880 }
881 foreach ($phoneTypes as $phoneType) {
882 $fields[] = [
883 'Individual',
884 'phone',
885 CRM_Core_PseudoConstant::getKey('CRM_Core_BAO_Phone', 'location_type_id', $locationType),
886 CRM_Core_PseudoConstant::getKey('CRM_Core_BAO_Phone', 'phone_type_id', $phoneType),
887 ];
888 foreach ($relationships as $contactID => $relationship) {
889 $fields[] = [
890 'Individual',
891 $relationship['relationship_type_id'] . '_a_b',
892 'phone_type_id',
893 CRM_Core_PseudoConstant::getKey('CRM_Core_BAO_Phone', 'location_type_id', $locationType),
894 CRM_Core_PseudoConstant::getKey('CRM_Core_BAO_Phone', 'phone_type_id', $phoneType),
895 ];
896 }
897 }
898 }
899 // @todo switch to just declaring the new format....
900 $mappedFields = [];
901 foreach ($fields as $field) {
902 $mappedFields[] = CRM_Core_BAO_Mapping::getMappingParams([], $field);
903 }
904 $this->doExportTest(['fields' => $mappedFields, 'ids' => [$this->contactIDs[0]]]);
905 foreach ($this->csv->getRecords() as $row) {
906 $this->assertEquals('BillingMobile3', $row['Billing-Phone-Mobile']);
907 $this->assertEquals('', $row['Billing-Phone-Phone']);
908 $this->assertEquals('Phone', $row['Spouse of-Phone Type']);
909 $this->assertEquals('Mobile', $row['Phone Type']);
910 $this->assertEquals('Mobile', $row['Billing-Phone Type']);
911 }
912 }
913
914 /**
915 * Export City against multiple location types.
916 *
917 * @throws \CRM_Core_Exception
918 * @throws \League\Csv\Exception
919 */
920 public function testExportAddressData() {
921 $this->diversifyLocationTypes();
922
923 $locationTypes = ['Billing' => 'Billing', 'Home' => 'Home', 'Main' => 'Méin', 'Other' => 'Other', 'Whare Kai' => 'Whare Kai'];
924
925 $this->contactIDs[] = $this->individualCreate();
926 $this->contactIDs[] = $this->individualCreate();
927 $this->contactIDs[] = $this->householdCreate();
928 $this->contactIDs[] = $this->organizationCreate();
929 $fields = [['name' => 'contact_id']];
930 foreach ($this->contactIDs as $contactID) {
931 foreach ($locationTypes as $locationName => $locationLabel) {
932 $this->callAPISuccess('Address', 'create', [
933 'contact_id' => $contactID,
934 'location_type_id' => $locationName,
935 'street_address' => $locationLabel . $contactID . 'street_address',
936 'city' => $locationLabel . $contactID . 'city',
937 'postal_code' => $locationLabel . $contactID . 'postal_code',
938 ]);
939 $fields[] = ['contact_type' => 'Individual', 'name' => 'city', 'location_type_id' => CRM_Core_PseudoConstant::getKey('CRM_Core_BAO_Address', 'location_type_id', $locationName)];
940 $fields[] = ['contact_type' => 'Individual', 'name' => 'street_address', 'location_type_id' => CRM_Core_PseudoConstant::getKey('CRM_Core_BAO_Address', 'location_type_id', $locationName)];
941 $fields[] = ['contact_type' => 'Individual', 'name' => 'postal_code', 'location_type_id' => CRM_Core_PseudoConstant::getKey('CRM_Core_BAO_Address', 'location_type_id', $locationName)];
942 }
943 }
944
945 $relationships = [
946 $this->contactIDs[1] => ['label' => 'Spouse of'],
947 $this->contactIDs[2] => ['label' => 'Household Member of'],
948 $this->contactIDs[3] => ['label' => 'Employee of'],
949 ];
950
951 foreach ($relationships as $contactID => $relationshipType) {
952 $relationshipTypeID = $this->callAPISuccess('RelationshipType', 'getvalue', ['label_a_b' => $relationshipType['label'], 'return' => 'id']);
953 $result = $this->callAPISuccess('Relationship', 'create', [
954 'contact_id_a' => $this->contactIDs[0],
955 'relationship_type_id' => $relationshipTypeID,
956 'contact_id_b' => $contactID,
957 ]);
958 $relationships[$contactID]['id'] = $result['id'];
959 $relationships[$contactID]['relationship_type_id'] = $relationshipTypeID;
960 }
961
962 // ' ' denotes primary location type.
963 foreach (array_keys(array_merge($locationTypes, [' ' => ['Primary']])) as $locationType) {
964 foreach ($relationships as $contactID => $relationship) {
965 $fields[] = [
966 'contact_type' => 'Individual',
967 'relationship_type_id' => $relationship['relationship_type_id'],
968 'relationship_direction' => 'a_b',
969 'name' => 'city',
970 'location_type_id' => CRM_Core_PseudoConstant::getKey('CRM_Core_BAO_IM', 'location_type_id', $locationType),
971 ];
972 }
973 }
974
975 $this->doExportTest(['fields' => $fields]);
976
977 foreach ($this->csv as $row) {
978 $contactID = (int) $row['Contact ID'];
979 $this->assertEquals('Méin' . $contactID . 'city', $row['Main-City']);
980 $this->assertEquals('Billing' . $contactID . 'street_address', $row['Billing-Street Address']);
981 $this->assertEquals('Whare Kai' . $contactID . 'postal_code', $row['Whare Kai-Postal Code']);
982 foreach ($relationships as $relatedContactID => $relationship) {
983 $value = ($contactID === $this->contactIDs[0]) ? 'Méin' . $relatedContactID . 'city' : '';
984 $this->assertEquals($value, $row[$relationship['label'] . '-Main-City'], 'checking ' . $relationship['label'] . '-Main-City');
985 }
986 }
987
988 $this->assertEquals([
989 'contact_id' => 'contact_id varchar(16)',
990 'billing_city' => 'billing_city varchar(64)',
991 'billing_street_address' => 'billing_street_address varchar(96)',
992 'billing_postal_code' => 'billing_postal_code varchar(64)',
993 'home_city' => 'home_city varchar(64)',
994 'home_street_address' => 'home_street_address varchar(96)',
995 'home_postal_code' => 'home_postal_code varchar(64)',
996 'main_city' => 'main_city varchar(64)',
997 'main_street_address' => 'main_street_address varchar(96)',
998 'main_postal_code' => 'main_postal_code varchar(64)',
999 'other_city' => 'other_city varchar(64)',
1000 'other_street_address' => 'other_street_address varchar(96)',
1001 'other_postal_code' => 'other_postal_code varchar(64)',
1002 'whare_kai_city' => 'whare_kai_city varchar(64)',
1003 'whare_kai_street_address' => 'whare_kai_street_address varchar(96)',
1004 'whare_kai_postal_code' => 'whare_kai_postal_code varchar(64)',
1005 '2_a_b_billing_city' => '2_a_b_billing_city varchar(64)',
1006 '2_a_b_home_city' => '2_a_b_home_city varchar(64)',
1007 '2_a_b_main_city' => '2_a_b_main_city varchar(64)',
1008 '2_a_b_other_city' => '2_a_b_other_city varchar(64)',
1009 '2_a_b_whare_kai_city' => '2_a_b_whare_kai_city varchar(64)',
1010 '2_a_b_city' => '2_a_b_city varchar(64)',
1011 '8_a_b_billing_city' => '8_a_b_billing_city varchar(64)',
1012 '8_a_b_home_city' => '8_a_b_home_city varchar(64)',
1013 '8_a_b_main_city' => '8_a_b_main_city varchar(64)',
1014 '8_a_b_other_city' => '8_a_b_other_city varchar(64)',
1015 '8_a_b_whare_kai_city' => '8_a_b_whare_kai_city varchar(64)',
1016 '8_a_b_city' => '8_a_b_city varchar(64)',
1017 '5_a_b_billing_city' => '5_a_b_billing_city varchar(64)',
1018 '5_a_b_home_city' => '5_a_b_home_city varchar(64)',
1019 '5_a_b_main_city' => '5_a_b_main_city varchar(64)',
1020 '5_a_b_other_city' => '5_a_b_other_city varchar(64)',
1021 '5_a_b_whare_kai_city' => '5_a_b_whare_kai_city varchar(64)',
1022 '5_a_b_city' => '5_a_b_city varchar(64)',
1023 ], $this->processor->getSQLColumns());
1024 }
1025
1026 /**
1027 * Test master_address_id field when no merge is in play.
1028 *
1029 * @throws \CRM_Core_Exception
1030 * @throws \League\Csv\Exception
1031 */
1032 public function testExportMasterAddress() {
1033 $this->setUpContactExportData();
1034
1035 //export the master address for contact B
1036 $selectedFields = [
1037 ['contact_type' => 'Individual', 'name' => 'master_id', 'location_type_id' => 1],
1038 ];
1039 $this->doExportTest([
1040 'fields' => $selectedFields,
1041 'ids' => [$this->contactIDs[1]],
1042 ]);
1043 $row = $this->csv->fetchOne();
1044 $this->assertEquals(CRM_Contact_BAO_Contact::getMasterDisplayName($this->masterAddressID), $row['Home-Master Address Belongs To']);
1045 }
1046
1047 /**
1048 * Test merging same address when specifying fields.
1049 *
1050 * @throws \CRM_Core_Exception
1051 * @throws \League\Csv\Exception
1052 */
1053 public function testMergeSameAddressSpecifyFields() {
1054 $this->setUpContactSameAddressExportData();
1055 $this->doExportTest(['mergeSameAddress' => TRUE, 'fields' => [['contact_type' => 'Individual', 'name' => 'master_id', 'location_type_id' => 1]]]);
1056 }
1057
1058 /**
1059 * Test the merge same address option.
1060 *
1061 * @throws \CRM_Core_Exception
1062 * @throws \League\Csv\Exception
1063 */
1064 public function testMergeSameAddress() {
1065 $this->setUpContactSameAddressExportData();
1066 $this->doExportTest(['mergeSameAddress' => TRUE]);
1067 // ie 2 merged, one extra.
1068 $this->assertCount(2, $this->csv);
1069 $expected = [
1070 'Contact ID' => $this->contactIDs[0],
1071 'Contact Type' => 'Individual',
1072 'Contact Subtype' => '',
1073 'Do Not Email' => '',
1074 'Do Not Phone' => '',
1075 'Do Not Mail' => '',
1076 'Do Not Sms' => '',
1077 'Do Not Trade' => '',
1078 'No Bulk Emails (User Opt Out)' => '',
1079 'Legal Identifier' => '',
1080 'External Identifier' => '',
1081 'Sort Name' => 'Anderson, Anthony',
1082 'Display Name' => 'Mr. Anthony Anderson II',
1083 'Nickname' => '',
1084 'Legal Name' => '',
1085 'Image Url' => '',
1086 'Preferred Communication Method' => '',
1087 'Preferred Language' => 'en_US',
1088 'Preferred Mail Format' => 'Both',
1089 'Contact Hash' => 'e9bd0913cc05cc5aeae69ba04ee3be84',
1090 'Contact Source' => '',
1091 'First Name' => 'Anthony',
1092 'Middle Name' => 'J.',
1093 'Last Name' => 'Anderson',
1094 'Individual Prefix' => 'Mr.',
1095 'Individual Suffix' => 'II',
1096 'Formal Title' => '',
1097 'Communication Style' => 'Formal',
1098 'Email Greeting ID' => '1',
1099 'Postal Greeting ID' => '1',
1100 'Addressee ID' => '1',
1101 'Job Title' => '',
1102 'Gender' => 'Female',
1103 'Birth Date' => '',
1104 'Deceased' => '',
1105 'Deceased Date' => '',
1106 'Household Name' => '',
1107 'Organization Name' => '',
1108 'Sic Code' => '',
1109 'Unique ID (OpenID)' => '',
1110 'Current Employer ID' => '',
1111 'Contact is in Trash' => '',
1112 'Created Date' => '2019-07-11 10:28:15',
1113 'Modified Date' => '2019-07-11 10:28:15',
1114 'Addressee' => 'Mr. Anthony J. Anderson II, Dr. Sarah J. Smith II',
1115 'Email Greeting' => 'Dear Anthony, Sarah',
1116 'Postal Greeting' => 'Dear Anthony, Sarah',
1117 'Current Employer' => '',
1118 'Location Type' => 'Home',
1119 'Street Address' => 'Ambachtstraat 23',
1120 'Street Number' => '',
1121 'Street Number Suffix' => '',
1122 'Street Name' => '',
1123 'Street Unit' => '',
1124 'Supplemental Address 1' => '',
1125 'Supplemental Address 2' => '',
1126 'Supplemental Address 3' => '',
1127 'City' => 'Brummen',
1128 'Postal Code Suffix' => '',
1129 'Postal Code' => '6971 BN',
1130 'Latitude' => '',
1131 'Longitude' => '',
1132 'Address Name' => '',
1133 'Master Address Belongs To' => '',
1134 'County' => '',
1135 'State' => '',
1136 'Country' => 'Netherlands',
1137 'Phone' => '',
1138 'Phone Extension' => '',
1139 'Phone Type' => '',
1140 'Email' => 'home@example.com',
1141 'On Hold' => '',
1142 'Use for Bulk Mail' => '',
1143 'Signature Text' => '',
1144 'Signature Html' => '',
1145 'IM Provider' => '',
1146 'IM Screen Name' => '',
1147 'OpenID' => '',
1148 'World Region' => 'Europe and Central Asia',
1149 'Website' => '',
1150 'Group(s)' => '',
1151 'Tag(s)' => '',
1152 'Note(s)' => '',
1153 ];
1154 $this->assertExpectedOutput($expected, $this->csv->fetchOne());
1155 }
1156
1157 /**
1158 * Tests the options for greeting templates when choosing to merge same address.
1159 *
1160 * @throws \CRM_Core_Exception
1161 * @throws \League\Csv\Exception
1162 */
1163 public function testMergeSameAddressGreetingOptions() {
1164 $this->setUpContactSameAddressExportData();
1165 $this->callAPISuccess('OptionValue', 'create', [
1166 'option_group_id' => 'postal_greeting',
1167 'label' => '{contact.individual_suffix} {contact.last_name} and first is {contact.first_name} ',
1168 // This hard coded number makes it available for export use.
1169 'filter' => 4,
1170 ]);
1171 $this->doExportTest([
1172 'mergeSameAddress' => TRUE,
1173 'exportParams' => [
1174 'postal_greeting' => '2',
1175 'postal_greeting_other' => '',
1176 'addressee' => '2',
1177 'addressee_other' => 'random string {contact.display_name}',
1178 'mergeOption' => '1',
1179 'additional_group' => '',
1180 'mapping' => '',
1181 ],
1182 ]);
1183 $this->assertExpectedOutput([
1184 'Addressee' => 'random string Mr. Anthony Anderson II, Dr. Sarah Smith II',
1185 'Email Greeting' => 'II Anderson and first is Anthony , II Smith Sarah ',
1186 'Postal Greeting' => 'II Anderson and first is Anthony , II Smith Sarah ',
1187 ], $this->csv->fetchOne());
1188 // 3 contacts merged to 2.
1189 $this->assertCount(2, $this->csv);
1190 }
1191
1192 /**
1193 * Test exporting when no rows are retrieved.
1194 *
1195 * @throws \CRM_Core_Exception
1196 * @throws \League\Csv\Exception
1197 */
1198 public function testExportNoRows() {
1199 $contactA = $this->callAPISuccess('contact', 'create', [
1200 'first_name' => 'John',
1201 'last_name' => 'Doe',
1202 'contact_type' => 'Individual',
1203 ]);
1204 $this->doExportTest([
1205 'selectAll' => TRUE,
1206 'ids' => [$contactA['id']],
1207 'exportParams' => [
1208 'postal_mailing_export' => [
1209 'postal_mailing_export' => TRUE,
1210 ],
1211 'mergeSameAddress' => TRUE,
1212 ],
1213 ]);
1214 $this->assertEquals('Contact ID', $this->csv->getHeader()[0]);
1215 }
1216
1217 /**
1218 * Test that deceased and do not mail contacts are removed from contacts before
1219 *
1220 * @dataProvider getReasonsNotToMail
1221 *
1222 * @param array $reason
1223 * @param array $addressReason
1224 *
1225 * @throws \CRM_Core_Exception
1226 * @throws \League\Csv\Exception
1227 */
1228 public function testExportDeceasedDoNotMail($reason, $addressReason) {
1229 $contactA = $this->callAPISuccess('contact', 'create', [
1230 'first_name' => 'John',
1231 'last_name' => 'Doe',
1232 'contact_type' => 'Individual',
1233 ]);
1234
1235 $contactB = $this->callAPISuccess('contact', 'create', array_merge([
1236 'first_name' => 'Jane',
1237 'last_name' => 'Doe',
1238 'contact_type' => 'Individual',
1239 ], $reason));
1240
1241 // Create another contact not included in the exporrt set.
1242 $this->callAPISuccess('contact', 'create', array_merge([
1243 'first_name' => 'Janet',
1244 'last_name' => 'Doe',
1245 'contact_type' => 'Individual',
1246 'api.address.create' => ['supplemental_address_1' => 'An address'],
1247 ], $reason));
1248
1249 // Create another contact not included in the exporrt set.
1250 $this->callAPISuccess('contact', 'create', array_merge([
1251 'first_name' => 'Janice',
1252 'last_name' => 'Doe',
1253 'contact_type' => 'Individual',
1254 ], $reason));
1255
1256 //create address for contact A
1257 $this->callAPISuccess('address', 'create', [
1258 'contact_id' => $contactA['id'],
1259 'location_type_id' => 'Home',
1260 'street_address' => 'ABC 12',
1261 'postal_code' => '123 AB',
1262 'country_id' => '1152',
1263 'city' => 'ABC',
1264 'is_primary' => 1,
1265 ]);
1266
1267 //create address for contact B
1268 $this->callAPISuccess('address', 'create', array_merge([
1269 'contact_id' => $contactB['id'],
1270 'location_type_id' => 'Home',
1271 'street_address' => 'ABC 12',
1272 'postal_code' => '123 AB',
1273 'country_id' => '1152',
1274 'city' => 'ABC',
1275 'is_primary' => 1,
1276 ], $addressReason));
1277
1278 $this->doExportTest([
1279 'selectAll' => TRUE,
1280 'ids' => [$contactA['id'], $contactB['id']],
1281 'exportParams' => [
1282 'postal_mailing_export' => [
1283 'postal_mailing_export' => TRUE,
1284 ],
1285 'mergeSameAddress' => TRUE,
1286 ],
1287 ]);
1288 $row = $this->csv->fetchOne(0);
1289
1290 $this->assertTrue(!in_array('Stage', $this->processor->getHeaderRows()));
1291 $this->assertEquals('Dear John', $row['Email Greeting']);
1292 $this->assertCount(1, $this->csv);
1293 }
1294
1295 /**
1296 * Get reasons that a contact is not postalable.
1297 *
1298 * @return array
1299 */
1300 public function getReasonsNotToMail() {
1301 return [
1302 [['is_deceased' => 1], []],
1303 [['do_not_mail' => 1], []],
1304 [[], ['street_address' => '']],
1305 ];
1306 }
1307
1308 /**
1309 * Set up household for tests.
1310 *
1311 * @return array
1312 *
1313 * @throws CRM_Core_Exception
1314 */
1315 protected function setUpHousehold() {
1316 $this->setUpContactExportData();
1317 $householdID = $this->householdCreate([
1318 'source' => 'household sauce',
1319 'api.Address.create' => [
1320 'city' => 'Portland',
1321 'state_province_id' => 'Maine',
1322 'location_type_id' => 'Home',
1323 ],
1324 ]);
1325
1326 $relationshipTypes = $this->callAPISuccess('RelationshipType', 'get', [])['values'];
1327 $houseHoldTypeID = NULL;
1328 foreach ($relationshipTypes as $id => $relationshipType) {
1329 if ($relationshipType['name_a_b'] === 'Household Member of') {
1330 $houseHoldTypeID = $relationshipType['id'];
1331 }
1332 }
1333 $this->callAPISuccess('Relationship', 'create', [
1334 'contact_id_a' => $this->contactIDs[0],
1335 'contact_id_b' => $householdID,
1336 'relationship_type_id' => $houseHoldTypeID,
1337 ]);
1338 $this->callAPISuccess('Relationship', 'create', [
1339 'contact_id_a' => $this->contactIDs[1],
1340 'contact_id_b' => $householdID,
1341 'relationship_type_id' => $houseHoldTypeID,
1342 ]);
1343 return [$householdID, $houseHoldTypeID];
1344 }
1345
1346 /**
1347 * Ensure component is enabled.
1348 *
1349 * @param int $exportMode
1350 */
1351 public function ensureComponentIsEnabled($exportMode) {
1352 if ($exportMode === CRM_Export_Form_Select::CASE_EXPORT) {
1353 CRM_Core_BAO_ConfigSetting::enableComponent('CiviCase');
1354 }
1355 }
1356
1357 /**
1358 * Test our export all field metadata retrieval.
1359 *
1360 * @dataProvider additionalFieldsDataProvider
1361 *
1362 * @param int $exportMode
1363 * @param $expected
1364 */
1365 public function testAdditionalReturnProperties($exportMode, $expected) {
1366 $this->ensureComponentIsEnabled($exportMode);
1367 $processor = new CRM_Export_BAO_ExportProcessor($exportMode, NULL, 'AND');
1368 $metadata = $processor->getAdditionalReturnProperties();
1369 $this->assertEquals($expected, $metadata);
1370 }
1371
1372 /**
1373 * Test our export all field metadata retrieval.
1374 *
1375 * @dataProvider allFieldsDataProvider
1376 *
1377 * @param int $exportMode
1378 * @param $expected
1379 */
1380 public function testDefaultReturnProperties($exportMode, $expected) {
1381 $this->ensureComponentIsEnabled($exportMode);
1382 $processor = new CRM_Export_BAO_ExportProcessor($exportMode, NULL, 'AND');
1383 $metadata = $processor->getDefaultReturnProperties();
1384 $this->assertEquals($expected, $metadata);
1385 }
1386
1387 /**
1388 * Get fields returned from additionalFields function.
1389 *
1390 * @return array
1391 */
1392 public function additionalFieldsDataProvider() {
1393 return [
1394 [
1395 'anything that will then be defaulting ton contact',
1396 $this->getExtraReturnProperties(),
1397 ],
1398 [
1399 CRM_Export_Form_Select::ACTIVITY_EXPORT,
1400 array_merge($this->getExtraReturnProperties(), $this->getActivityReturnProperties()),
1401 ],
1402 [
1403 CRM_Export_Form_Select::CASE_EXPORT,
1404 array_merge($this->getExtraReturnProperties(), $this->getCaseReturnProperties()),
1405 ],
1406 [
1407 CRM_Export_Form_Select::CONTRIBUTE_EXPORT,
1408 array_merge($this->getExtraReturnProperties(), $this->getContributionReturnProperties()),
1409 ],
1410 [
1411 CRM_Export_Form_Select::EVENT_EXPORT,
1412 array_merge($this->getExtraReturnProperties(), $this->getEventReturnProperties()),
1413 ],
1414 [
1415 CRM_Export_Form_Select::MEMBER_EXPORT,
1416 array_merge($this->getExtraReturnProperties(), $this->getMembershipReturnProperties()),
1417 ],
1418 [
1419 CRM_Export_Form_Select::PLEDGE_EXPORT,
1420 array_merge($this->getExtraReturnProperties(), $this->getPledgeReturnProperties()),
1421 ],
1422
1423 ];
1424 }
1425
1426 /**
1427 * get data for testing field metadata by query mode.
1428 */
1429 public function allFieldsDataProvider() {
1430 return [
1431 [
1432 'anything that will then be defaulting ton contact',
1433 $this->getBasicReturnProperties(TRUE),
1434 ],
1435 [
1436 CRM_Export_Form_Select::ACTIVITY_EXPORT,
1437 array_merge($this->getBasicReturnProperties(FALSE), $this->getActivityReturnProperties()),
1438 ],
1439 [
1440 CRM_Export_Form_Select::CASE_EXPORT,
1441 array_merge($this->getBasicReturnProperties(FALSE), $this->getCaseReturnProperties()),
1442 ],
1443 [
1444 CRM_Export_Form_Select::CONTRIBUTE_EXPORT,
1445 array_merge($this->getBasicReturnProperties(FALSE), $this->getContributionReturnProperties()),
1446 ],
1447 [
1448 CRM_Export_Form_Select::EVENT_EXPORT,
1449 array_merge($this->getBasicReturnProperties(FALSE), $this->getEventReturnProperties()),
1450 ],
1451 [
1452 CRM_Export_Form_Select::MEMBER_EXPORT,
1453 array_merge($this->getBasicReturnProperties(FALSE), $this->getMembershipReturnProperties()),
1454 ],
1455 [
1456 CRM_Export_Form_Select::PLEDGE_EXPORT,
1457 array_merge($this->getBasicReturnProperties(FALSE), $this->getPledgeReturnProperties()),
1458 ],
1459 ];
1460 }
1461
1462 /**
1463 * Get return properties manually added in.
1464 */
1465 public function getExtraReturnProperties() {
1466 return [];
1467 }
1468
1469 /**
1470 * Get basic return properties.
1471 *
1472 * @param bool $isContactMode
1473 * Are we in contact mode or not
1474 *
1475 * @return array
1476 */
1477 protected function getBasicReturnProperties($isContactMode) {
1478 $returnProperties = [
1479 'id' => 1,
1480 'contact_type' => 1,
1481 'contact_sub_type' => 1,
1482 'do_not_email' => 1,
1483 'do_not_phone' => 1,
1484 'do_not_mail' => 1,
1485 'do_not_sms' => 1,
1486 'do_not_trade' => 1,
1487 'is_opt_out' => 1,
1488 'legal_identifier' => 1,
1489 'external_identifier' => 1,
1490 'sort_name' => 1,
1491 'display_name' => 1,
1492 'nick_name' => 1,
1493 'legal_name' => 1,
1494 'image_URL' => 1,
1495 'preferred_communication_method' => 1,
1496 'preferred_language' => 1,
1497 'preferred_mail_format' => 1,
1498 'hash' => 1,
1499 'contact_source' => 1,
1500 'first_name' => 1,
1501 'middle_name' => 1,
1502 'last_name' => 1,
1503 'prefix_id' => 1,
1504 'suffix_id' => 1,
1505 'formal_title' => 1,
1506 'communication_style_id' => 1,
1507 'email_greeting_id' => 1,
1508 'postal_greeting_id' => 1,
1509 'addressee_id' => 1,
1510 'job_title' => 1,
1511 'gender_id' => 1,
1512 'birth_date' => 1,
1513 'is_deceased' => 1,
1514 'deceased_date' => 1,
1515 'household_name' => 1,
1516 'organization_name' => 1,
1517 'sic_code' => 1,
1518 'user_unique_id' => 1,
1519 'current_employer_id' => 1,
1520 'contact_is_deleted' => 1,
1521 'created_date' => 1,
1522 'modified_date' => 1,
1523 'addressee' => 1,
1524 'email_greeting' => 1,
1525 'postal_greeting' => 1,
1526 'current_employer' => 1,
1527 'location_type' => 1,
1528 'street_address' => 1,
1529 'street_number' => 1,
1530 'street_number_suffix' => 1,
1531 'street_name' => 1,
1532 'street_unit' => 1,
1533 'supplemental_address_1' => 1,
1534 'supplemental_address_2' => 1,
1535 'supplemental_address_3' => 1,
1536 'city' => 1,
1537 'postal_code_suffix' => 1,
1538 'postal_code' => 1,
1539 'geo_code_1' => 1,
1540 'geo_code_2' => 1,
1541 'manual_geo_code' => 1,
1542 'address_name' => 1,
1543 'master_id' => 1,
1544 'county' => 1,
1545 'state_province' => 1,
1546 'country' => 1,
1547 'phone' => 1,
1548 'phone_ext' => 1,
1549 'email' => 1,
1550 'on_hold' => 1,
1551 'is_bulkmail' => 1,
1552 'signature_text' => 1,
1553 'signature_html' => 1,
1554 'im_provider' => 1,
1555 'im' => 1,
1556 'openid' => 1,
1557 'world_region' => 1,
1558 'url' => 1,
1559 'groups' => 1,
1560 'tags' => 1,
1561 'notes' => 1,
1562 'phone_type_id' => 1,
1563 ];
1564 if (!$isContactMode) {
1565 unset($returnProperties['groups']);
1566 unset($returnProperties['tags']);
1567 unset($returnProperties['notes']);
1568 }
1569 return $returnProperties;
1570 }
1571
1572 /**
1573 * Get return properties for pledges.
1574 *
1575 * @return array
1576 */
1577 public function getPledgeReturnProperties() {
1578 return [
1579 'contact_type' => 1,
1580 'contact_sub_type' => 1,
1581 'sort_name' => 1,
1582 'display_name' => 1,
1583 'pledge_id' => 1,
1584 'pledge_amount' => 1,
1585 'pledge_total_paid' => 1,
1586 'pledge_create_date' => 1,
1587 'pledge_start_date' => 1,
1588 'pledge_next_pay_date' => 1,
1589 'pledge_next_pay_amount' => 1,
1590 'pledge_status' => 1,
1591 'pledge_is_test' => 1,
1592 'pledge_contribution_page_id' => 1,
1593 'pledge_financial_type' => 1,
1594 'pledge_frequency_interval' => 1,
1595 'pledge_frequency_unit' => 1,
1596 'pledge_currency' => 1,
1597 'pledge_campaign_id' => 1,
1598 'pledge_balance_amount' => 1,
1599 'pledge_payment_id' => 1,
1600 'pledge_payment_scheduled_amount' => 1,
1601 'pledge_payment_scheduled_date' => 1,
1602 'pledge_payment_paid_amount' => 1,
1603 'pledge_payment_paid_date' => 1,
1604 'pledge_payment_reminder_date' => 1,
1605 'pledge_payment_reminder_count' => 1,
1606 'pledge_payment_status' => 1,
1607 ];
1608 }
1609
1610 /**
1611 * Get membership return properties.
1612 *
1613 * @return array
1614 */
1615 public function getMembershipReturnProperties() {
1616 return [
1617 'contact_type' => 1,
1618 'contact_sub_type' => 1,
1619 'sort_name' => 1,
1620 'display_name' => 1,
1621 'membership_type' => 1,
1622 'member_is_test' => 1,
1623 'member_is_pay_later' => 1,
1624 'membership_join_date' => 1,
1625 'membership_start_date' => 1,
1626 'membership_end_date' => 1,
1627 'membership_source' => 1,
1628 'membership_status' => 1,
1629 'membership_id' => 1,
1630 'owner_membership_id' => 1,
1631 'max_related' => 1,
1632 'membership_recur_id' => 1,
1633 'member_campaign_id' => 1,
1634 'member_is_override' => 1,
1635 ];
1636 }
1637
1638 /**
1639 * Get return properties for events.
1640 *
1641 * @return array
1642 */
1643 public function getEventReturnProperties() {
1644 return [
1645 'contact_type' => 1,
1646 'contact_sub_type' => 1,
1647 'sort_name' => 1,
1648 'display_name' => 1,
1649 'event_id' => 1,
1650 'event_title' => 1,
1651 'event_start_date' => 1,
1652 'event_end_date' => 1,
1653 'event_type' => 1,
1654 'participant_id' => 1,
1655 'participant_status' => 1,
1656 'participant_status_id' => 1,
1657 'participant_role' => 1,
1658 'participant_role_id' => 1,
1659 'participant_note' => 1,
1660 'participant_register_date' => 1,
1661 'participant_source' => 1,
1662 'participant_fee_level' => 1,
1663 'participant_is_test' => 1,
1664 'participant_is_pay_later' => 1,
1665 'participant_fee_amount' => 1,
1666 'participant_discount_name' => 1,
1667 'participant_fee_currency' => 1,
1668 'participant_registered_by_id' => 1,
1669 'participant_campaign_id' => 1,
1670 ];
1671 }
1672
1673 /**
1674 * Get return properties for activities.
1675 *
1676 * @return array
1677 */
1678 public function getActivityReturnProperties() {
1679 return [
1680 'activity_id' => 1,
1681 'contact_type' => 1,
1682 'contact_sub_type' => 1,
1683 'sort_name' => 1,
1684 'display_name' => 1,
1685 'activity_type' => 1,
1686 'activity_type_id' => 1,
1687 'activity_subject' => 1,
1688 'activity_date_time' => 1,
1689 'activity_duration' => 1,
1690 'activity_location' => 1,
1691 'activity_details' => 1,
1692 'activity_status' => 1,
1693 'activity_priority' => 1,
1694 'source_contact' => 1,
1695 'source_record_id' => 1,
1696 'activity_is_test' => 1,
1697 'activity_campaign_id' => 1,
1698 'result' => 1,
1699 'activity_engagement_level' => 1,
1700 'parent_id' => 1,
1701 ];
1702 }
1703
1704 /**
1705 * Get return properties for Case.
1706 *
1707 * @return array
1708 */
1709 public function getCaseReturnProperties() {
1710 return [
1711 'contact_type' => 1,
1712 'contact_sub_type' => 1,
1713 'sort_name' => 1,
1714 'display_name' => 1,
1715 'phone' => 1,
1716 'case_start_date' => 1,
1717 'case_end_date' => 1,
1718 'case_subject' => 1,
1719 'case_source_contact_id' => 1,
1720 'case_activity_status' => 1,
1721 'case_activity_duration' => 1,
1722 'case_activity_medium_id' => 1,
1723 'case_activity_details' => 1,
1724 'case_activity_is_auto' => 1,
1725 'contact_id' => 1,
1726 'case_id' => 1,
1727 'case_activity_subject' => 1,
1728 'case_status' => 1,
1729 'case_type' => 1,
1730 'case_role' => 1,
1731 'case_deleted' => 1,
1732 'case_recent_activity_date' => 1,
1733 'case_recent_activity_type' => 1,
1734 'case_scheduled_activity_date' => 1,
1735 ];
1736 }
1737
1738 /**
1739 * Get return properties for contribution.
1740 *
1741 * @return array
1742 */
1743 public function getContributionReturnProperties() {
1744 return [
1745 'contact_type' => 1,
1746 'contact_sub_type' => 1,
1747 'sort_name' => 1,
1748 'display_name' => 1,
1749 'financial_type' => 1,
1750 'contribution_source' => 1,
1751 'receive_date' => 1,
1752 'thankyou_date' => 1,
1753 'contribution_cancel_date' => 1,
1754 'total_amount' => 1,
1755 'accounting_code' => 1,
1756 'payment_instrument' => 1,
1757 'payment_instrument_id' => 1,
1758 'contribution_check_number' => 1,
1759 'non_deductible_amount' => 1,
1760 'fee_amount' => 1,
1761 'net_amount' => 1,
1762 'trxn_id' => 1,
1763 'invoice_id' => 1,
1764 'invoice_number' => 1,
1765 'currency' => 1,
1766 'cancel_reason' => 1,
1767 'receipt_date' => 1,
1768 'is_test' => 1,
1769 'is_pay_later' => 1,
1770 'contribution_status' => 1,
1771 'contribution_recur_id' => 1,
1772 'amount_level' => 1,
1773 'contribution_note' => 1,
1774 'contribution_batch' => 1,
1775 'contribution_campaign_title' => 1,
1776 'contribution_campaign_id' => 1,
1777 'contribution_soft_credit_name' => 1,
1778 'contribution_soft_credit_amount' => 1,
1779 'contribution_soft_credit_type' => 1,
1780 'contribution_soft_credit_contact_id' => 1,
1781 'contribution_soft_credit_contribution_id' => 1,
1782 ];
1783 }
1784
1785 /**
1786 * Test the column definition when 'all' fields defined.
1787 *
1788 * @param int $exportMode
1789 * @param array $expected
1790 * @param array $expectedHeaders
1791 *
1792 * @throws \CRM_Core_Exception
1793 * @throws \League\Csv\Exception
1794 *
1795 * @dataProvider getSqlColumnsOutput
1796 */
1797 public function testGetSQLColumnsAndHeaders($exportMode, $expected, $expectedHeaders) {
1798 $this->ensureComponentIsEnabled($exportMode);
1799 // We need some data so that we can get to the end of the export
1800 // function. Hopefully one day that won't be required to get metadata info out.
1801 // eventually aspire to call $provider->getSQLColumns straight after it
1802 // is intiated.
1803 $this->setupBaseExportData($exportMode);
1804 $this->doExportTest(['selectAll' => TRUE, 'exportMode' => $exportMode, 'ids' => [1]]);
1805 $this->assertEquals($expected, $this->processor->getSQLColumns());
1806 $this->assertEquals($expectedHeaders, $this->processor->getHeaderRows());
1807 }
1808
1809 /**
1810 * Test exported with data entry mis-fire.
1811 *
1812 * Not fatal error if data incomplete.
1813 *
1814 * https://lab.civicrm.org/dev/core/issues/819
1815 *
1816 * @throws \CRM_Core_Exception
1817 * @throws \League\Csv\Exception
1818 */
1819 public function testExportIncompleteSubmission() {
1820 $this->setUpContactExportData();
1821 $this->doExportTest(['fields' => [['contact_type' => 'Individual', 'name' => '']], 'ids' => [$this->contactIDs[1]]]);
1822 }
1823
1824 /**
1825 * Test exported with fields to output specified.
1826 *
1827 * @dataProvider getAllSpecifiableReturnFields
1828 *
1829 * @param int $exportMode
1830 * @param array $selectedFields
1831 * @param array $expected
1832 *
1833 * @throws \CRM_Core_Exception
1834 * @throws \League\Csv\Exception
1835 */
1836 public function testExportSpecifyFields($exportMode, $selectedFields, $expected) {
1837 $this->ensureComponentIsEnabled($exportMode);
1838 $this->setUpContributionExportData();
1839 $this->doExportTest(['fields' => $selectedFields, 'ids' => [$this->contactIDs[1]], 'exportMode' => $exportMode]);
1840 $this->assertEquals($expected, $this->processor->getSQLColumns());
1841 }
1842
1843 /**
1844 * Test export fields when no payment fields to be exported.
1845 *
1846 * @throws \CRM_Core_Exception
1847 * @throws \League\Csv\Exception
1848 */
1849 public function textExportParticipantSpecifyFieldsNoPayment() {
1850 $selectedFields = $this->getAllSpecifiableParticipantReturnFields();
1851 foreach ($selectedFields as $index => $field) {
1852 if (substr($field[1], 0, 22) === 'componentPaymentField_') {
1853 unset($selectedFields[$index]);
1854 }
1855 }
1856
1857 $expected = $this->getAllSpecifiableParticipantReturnFields();
1858 foreach ($expected as $index => $field) {
1859 if (substr($index, 0, 22) === 'componentPaymentField_') {
1860 unset($expected[$index]);
1861 }
1862 }
1863 $this->doExportTest(['fields' => $selectedFields, 'ids' => [$this->contactIDs[1]], 'exportMode' => CRM_Export_Form_Select::EVENT_EXPORT]);
1864 $this->assertEquals($expected, $this->processor->getSQLColumns());
1865 }
1866
1867 /**
1868 * Get all return fields (@return array
1869 *
1870 * @todo - still being built up.
1871 *
1872 */
1873 public function getAllSpecifiableReturnFields() {
1874 return [
1875 [
1876 CRM_Export_Form_Select::EVENT_EXPORT,
1877 $this->getAllSpecifiableParticipantReturnFields(),
1878 $this->getAllSpecifiableParticipantReturnColumns(),
1879 ],
1880 ];
1881 }
1882
1883 /**
1884 * Get expected return column output for participant mode return all columns.
1885 *
1886 * @return array
1887 */
1888 public function getAllSpecifiableParticipantReturnColumns() {
1889 return [
1890 'participant_campaign_id' => 'participant_campaign_id varchar(16)',
1891 'participant_contact_id' => 'participant_contact_id varchar(16)',
1892 'componentpaymentfield_contribution_status' => 'componentpaymentfield_contribution_status varchar(255)',
1893 'currency' => 'currency varchar(3)',
1894 'componentpaymentfield_received_date' => 'componentpaymentfield_received_date varchar(32)',
1895 'default_role_id' => 'default_role_id varchar(16)',
1896 'participant_discount_name' => 'participant_discount_name varchar(16)',
1897 'event_id' => 'event_id varchar(16)',
1898 'event_end_date' => 'event_end_date varchar(32)',
1899 'event_start_date' => 'event_start_date varchar(32)',
1900 'template_title' => 'template_title varchar(255)',
1901 'event_title' => 'event_title varchar(255)',
1902 'participant_fee_amount' => 'participant_fee_amount varchar(32)',
1903 'participant_fee_currency' => 'participant_fee_currency varchar(3)',
1904 'fee_label' => 'fee_label varchar(255)',
1905 'participant_fee_level' => 'participant_fee_level longtext',
1906 'participant_is_pay_later' => 'participant_is_pay_later varchar(16)',
1907 'participant_id' => 'participant_id varchar(16)',
1908 'participant_note' => 'participant_note text',
1909 'participant_role_id' => 'participant_role_id varchar(128)',
1910 'participant_role' => 'participant_role varchar(255)',
1911 'participant_source' => 'participant_source varchar(128)',
1912 'participant_status_id' => 'participant_status_id varchar(16)',
1913 'participant_status' => 'participant_status varchar(255)',
1914 'participant_register_date' => 'participant_register_date varchar(32)',
1915 'participant_registered_by_id' => 'participant_registered_by_id varchar(16)',
1916 'participant_is_test' => 'participant_is_test varchar(16)',
1917 'componentpaymentfield_total_amount' => 'componentpaymentfield_total_amount varchar(32)',
1918 'componentpaymentfield_transaction_id' => 'componentpaymentfield_transaction_id varchar(255)',
1919 'transferred_to_contact_id' => 'transferred_to_contact_id varchar(16)',
1920 ];
1921 }
1922
1923 /**
1924 * @return array
1925 */
1926 public function getAllSpecifiableParticipantReturnFields() {
1927 return [
1928 0 =>
1929 [
1930 0 => 'Participant',
1931 'name' => '',
1932 ],
1933 1 =>
1934 [
1935 0 => 'Participant',
1936 'name' => 'participant_campaign_id',
1937 ],
1938 2 =>
1939 [
1940 0 => 'Participant',
1941 'name' => 'participant_contact_id',
1942 ],
1943 3 =>
1944 [
1945 0 => 'Participant',
1946 'name' => 'componentPaymentField_contribution_status',
1947 ],
1948 4 =>
1949 [
1950 0 => 'Participant',
1951 'name' => 'currency',
1952 ],
1953 5 =>
1954 [
1955 0 => 'Participant',
1956 'name' => 'componentPaymentField_received_date',
1957 ],
1958 6 =>
1959 [
1960 0 => 'Participant',
1961 'name' => 'default_role_id',
1962 ],
1963 7 =>
1964 [
1965 0 => 'Participant',
1966 'name' => 'participant_discount_name',
1967 ],
1968 8 =>
1969 [
1970 0 => 'Participant',
1971 'name' => 'event_id',
1972 ],
1973 9 =>
1974 [
1975 0 => 'Participant',
1976 'name' => 'event_end_date',
1977 ],
1978 10 =>
1979 [
1980 0 => 'Participant',
1981 'name' => 'event_start_date',
1982 ],
1983 11 =>
1984 [
1985 0 => 'Participant',
1986 'name' => 'template_title',
1987 ],
1988 12 =>
1989 [
1990 0 => 'Participant',
1991 'name' => 'event_title',
1992 ],
1993 13 =>
1994 [
1995 0 => 'Participant',
1996 'name' => 'participant_fee_amount',
1997 ],
1998 14 =>
1999 [
2000 0 => 'Participant',
2001 'name' => 'participant_fee_currency',
2002 ],
2003 15 =>
2004 [
2005 0 => 'Participant',
2006 'name' => 'fee_label',
2007 ],
2008 16 =>
2009 [
2010 0 => 'Participant',
2011 'name' => 'participant_fee_level',
2012 ],
2013 17 =>
2014 [
2015 0 => 'Participant',
2016 'name' => 'participant_is_pay_later',
2017 ],
2018 18 =>
2019 [
2020 0 => 'Participant',
2021 'name' => 'participant_id',
2022 ],
2023 19 =>
2024 [
2025 0 => 'Participant',
2026 'name' => 'participant_note',
2027 ],
2028 20 =>
2029 [
2030 0 => 'Participant',
2031 'name' => 'participant_role_id',
2032 ],
2033 21 =>
2034 [
2035 0 => 'Participant',
2036 'name' => 'participant_role',
2037 ],
2038 22 =>
2039 [
2040 0 => 'Participant',
2041 'name' => 'participant_source',
2042 ],
2043 23 =>
2044 [
2045 0 => 'Participant',
2046 'name' => 'participant_status_id',
2047 ],
2048 24 =>
2049 [
2050 0 => 'Participant',
2051 'name' => 'participant_status',
2052 ],
2053 25 =>
2054 [
2055 0 => 'Participant',
2056 'name' => 'participant_status',
2057 ],
2058 26 =>
2059 [
2060 0 => 'Participant',
2061 'name' => 'participant_register_date',
2062 ],
2063 27 =>
2064 [
2065 0 => 'Participant',
2066 'name' => 'participant_registered_by_id',
2067 ],
2068 28 =>
2069 [
2070 0 => 'Participant',
2071 'name' => 'participant_is_test',
2072 ],
2073 29 =>
2074 [
2075 0 => 'Participant',
2076 'name' => 'componentPaymentField_total_amount',
2077 ],
2078 30 =>
2079 [
2080 0 => 'Participant',
2081 'name' => 'componentPaymentField_transaction_id',
2082 ],
2083 31 =>
2084 [
2085 0 => 'Participant',
2086 'name' => 'transferred_to_contact_id',
2087 ],
2088 ];
2089 }
2090
2091 /**
2092 * @param string $exportMode
2093 */
2094 public function setupBaseExportData($exportMode) {
2095 $this->createLoggedInUser();
2096 if ($exportMode === CRM_Export_Form_Select::CASE_EXPORT) {
2097 $this->setupCaseExportData();
2098 }
2099 if ($exportMode === CRM_Export_Form_Select::CONTRIBUTE_EXPORT) {
2100 $this->setUpContributionExportData();
2101 }
2102 if ($exportMode === CRM_Export_Form_Select::MEMBER_EXPORT) {
2103 $this->setUpMembershipExportData();
2104 }
2105 if ($exportMode === CRM_Export_Form_Select::ACTIVITY_EXPORT) {
2106 $this->setUpActivityExportData();
2107 }
2108 }
2109
2110 /**
2111 * Get comprehensive sql columns output.
2112 *
2113 * @return array
2114 */
2115 public function getSqlColumnsOutput() {
2116 return [
2117 [
2118 'anything that will then be defaulting ton contact',
2119 $this->getBasicSqlColumnDefinition(TRUE),
2120 $this->getBasicHeaderDefinition(TRUE),
2121 ],
2122 [
2123 CRM_Export_Form_Select::ACTIVITY_EXPORT,
2124 array_merge($this->getBasicSqlColumnDefinition(FALSE), $this->getActivitySqlColumns()),
2125 array_merge($this->getBasicHeaderDefinition(FALSE), $this->getActivityHeaderDefinition()),
2126 ],
2127 [
2128 CRM_Export_Form_Select::CASE_EXPORT,
2129 array_merge($this->getBasicSqlColumnDefinition(FALSE), $this->getCaseSqlColumns()),
2130 array_merge($this->getBasicHeaderDefinition(FALSE), $this->getCaseHeaderDefinition()),
2131 ],
2132 [
2133 CRM_Export_Form_Select::CONTRIBUTE_EXPORT,
2134 array_merge($this->getBasicSqlColumnDefinition(FALSE), $this->getContributionSqlColumns()),
2135 array_merge($this->getBasicHeaderDefinition(FALSE), $this->getContributeHeaderDefinition()),
2136 ],
2137 [
2138 CRM_Export_Form_Select::EVENT_EXPORT,
2139 array_merge($this->getBasicSqlColumnDefinition(FALSE), $this->getParticipantSqlColumns()),
2140 array_merge($this->getBasicHeaderDefinition(FALSE), $this->getParticipantHeaderDefinition()),
2141 ],
2142 [
2143 CRM_Export_Form_Select::MEMBER_EXPORT,
2144 array_merge($this->getBasicSqlColumnDefinition(FALSE), $this->getMembershipSqlColumns()),
2145 array_merge($this->getBasicHeaderDefinition(FALSE), $this->getMemberHeaderDefinition()),
2146 ],
2147 [
2148 CRM_Export_Form_Select::PLEDGE_EXPORT,
2149 array_merge($this->getBasicSqlColumnDefinition(FALSE), $this->getPledgeSqlColumns()),
2150 array_merge($this->getBasicHeaderDefinition(FALSE), $this->getPledgeHeaderDefinition()),
2151 ],
2152
2153 ];
2154 }
2155
2156 /**
2157 * Get the header definition for exports.
2158 *
2159 * @param bool $isContactExport
2160 *
2161 * @return array
2162 */
2163 protected function getBasicHeaderDefinition($isContactExport) {
2164 $headers = [
2165 0 => 'Contact ID',
2166 1 => 'Contact Type',
2167 2 => 'Contact Subtype',
2168 3 => 'Do Not Email',
2169 4 => 'Do Not Phone',
2170 5 => 'Do Not Mail',
2171 6 => 'Do Not Sms',
2172 7 => 'Do Not Trade',
2173 8 => 'No Bulk Emails (User Opt Out)',
2174 9 => 'Legal Identifier',
2175 10 => 'External Identifier',
2176 11 => 'Sort Name',
2177 12 => 'Display Name',
2178 13 => 'Nickname',
2179 14 => 'Legal Name',
2180 15 => 'Image Url',
2181 16 => 'Preferred Communication Method',
2182 17 => 'Preferred Language',
2183 18 => 'Preferred Mail Format',
2184 19 => 'Contact Hash',
2185 20 => 'Contact Source',
2186 21 => 'First Name',
2187 22 => 'Middle Name',
2188 23 => 'Last Name',
2189 24 => 'Individual Prefix',
2190 25 => 'Individual Suffix',
2191 26 => 'Formal Title',
2192 27 => 'Communication Style',
2193 28 => 'Email Greeting ID',
2194 29 => 'Postal Greeting ID',
2195 30 => 'Addressee ID',
2196 31 => 'Job Title',
2197 32 => 'Gender',
2198 33 => 'Birth Date',
2199 34 => 'Deceased',
2200 35 => 'Deceased Date',
2201 36 => 'Household Name',
2202 37 => 'Organization Name',
2203 38 => 'Sic Code',
2204 39 => 'Unique ID (OpenID)',
2205 40 => 'Current Employer ID',
2206 41 => 'Contact is in Trash',
2207 42 => 'Created Date',
2208 43 => 'Modified Date',
2209 44 => 'Addressee',
2210 45 => 'Email Greeting',
2211 46 => 'Postal Greeting',
2212 47 => 'Current Employer',
2213 48 => 'Location Type',
2214 49 => 'Street Address',
2215 50 => 'Street Number',
2216 51 => 'Street Number Suffix',
2217 52 => 'Street Name',
2218 53 => 'Street Unit',
2219 54 => 'Supplemental Address 1',
2220 55 => 'Supplemental Address 2',
2221 56 => 'Supplemental Address 3',
2222 57 => 'City',
2223 58 => 'Postal Code Suffix',
2224 59 => 'Postal Code',
2225 60 => 'Latitude',
2226 61 => 'Longitude',
2227 62 => 'Is Manually Geocoded',
2228 63 => 'Address Name',
2229 64 => 'Master Address Belongs To',
2230 65 => 'County',
2231 66 => 'State',
2232 67 => 'Country',
2233 68 => 'Phone',
2234 69 => 'Phone Extension',
2235 70 => 'Phone Type',
2236 71 => 'Email',
2237 72 => 'On Hold',
2238 73 => 'Use for Bulk Mail',
2239 74 => 'Signature Text',
2240 75 => 'Signature Html',
2241 76 => 'IM Provider',
2242 77 => 'IM Screen Name',
2243 78 => 'OpenID',
2244 79 => 'World Region',
2245 80 => 'Website',
2246 81 => 'Group(s)',
2247 82 => 'Tag(s)',
2248 83 => 'Note(s)',
2249 ];
2250 if (!$isContactExport) {
2251 unset($headers[81]);
2252 unset($headers[82]);
2253 unset($headers[83]);
2254 }
2255 return $headers;
2256 }
2257
2258 /**
2259 * Get the definition for activity headers.
2260 *
2261 * @return array
2262 */
2263 protected function getActivityHeaderDefinition() {
2264 return [
2265 81 => 'Activity ID',
2266 82 => 'Activity Type',
2267 83 => 'Activity Type ID',
2268 84 => 'Subject',
2269 85 => 'Activity Date',
2270 86 => 'Duration',
2271 87 => 'Location',
2272 88 => 'Details',
2273 89 => 'Activity Status',
2274 90 => 'Activity Priority',
2275 91 => 'Source Contact',
2276 92 => 'source_record_id',
2277 93 => 'Test',
2278 94 => 'Campaign ID',
2279 95 => 'result',
2280 96 => 'Engagement Index',
2281 97 => 'parent_id',
2282 ];
2283 }
2284
2285 /**
2286 * Get the definition for case headers.
2287 *
2288 * @return array
2289 */
2290 protected function getCaseHeaderDefinition() {
2291 return [
2292 81 => 'Contact ID',
2293 82 => 'Case ID',
2294 83 => 'case_activity_subject',
2295 84 => 'Case Subject',
2296 85 => 'Case Status',
2297 86 => 'Case Type',
2298 87 => 'Role in Case',
2299 88 => 'Case is in the Trash',
2300 89 => 'case_recent_activity_date',
2301 90 => 'case_recent_activity_type',
2302 91 => 'case_scheduled_activity_date',
2303 92 => 'Case Start Date',
2304 93 => 'Case End Date',
2305 94 => 'case_source_contact_id',
2306 95 => 'case_activity_status',
2307 96 => 'case_activity_duration',
2308 97 => 'case_activity_medium_id',
2309 98 => 'case_activity_details',
2310 99 => 'case_activity_is_auto',
2311 ];
2312 }
2313
2314 /**
2315 * Get the definition for contribute headers.
2316 *
2317 * @return array
2318 */
2319 protected function getContributeHeaderDefinition() {
2320 return [
2321 81 => 'Financial Type',
2322 82 => 'Contribution Source',
2323 83 => 'Date Received',
2324 84 => 'Thank-you Date',
2325 85 => 'Cancelled / Refunded Date',
2326 86 => 'Total Amount',
2327 87 => 'Accounting Code',
2328 88 => 'Payment Methods',
2329 89 => 'Payment Method ID',
2330 90 => 'Check Number',
2331 91 => 'Non-deductible Amount',
2332 92 => 'Fee Amount',
2333 93 => 'Net Amount',
2334 94 => 'Transaction ID',
2335 95 => 'Invoice Reference',
2336 96 => 'Invoice Number',
2337 97 => 'Currency',
2338 98 => 'Cancellation / Refund Reason',
2339 99 => 'Receipt Date',
2340 100 => 'Test',
2341 101 => 'Is Pay Later',
2342 102 => 'Contribution Status',
2343 103 => 'Recurring Contribution ID',
2344 104 => 'Amount Label',
2345 105 => 'Contribution Note',
2346 106 => 'Batch Name',
2347 107 => 'Campaign Title',
2348 108 => 'Campaign ID',
2349 109 => 'Soft Credit For',
2350 110 => 'Soft Credit Amount',
2351 111 => 'Soft Credit Type',
2352 112 => 'Soft Credit For Contact ID',
2353 113 => 'Soft Credit For Contribution ID',
2354 ];
2355 }
2356
2357 /**
2358 * Get the definition for event headers.
2359 *
2360 * @return array
2361 */
2362 protected function getParticipantHeaderDefinition() {
2363 return [
2364 81 => 'Event',
2365 82 => 'Event Title',
2366 83 => 'Event Start Date',
2367 84 => 'Event End Date',
2368 85 => 'Event Type',
2369 86 => 'Participant ID',
2370 87 => 'Participant Status',
2371 88 => 'Participant Status Id',
2372 89 => 'Participant Role',
2373 90 => 'Participant Role Id',
2374 91 => 'Participant Note',
2375 92 => 'Register date',
2376 93 => 'Participant Source',
2377 94 => 'Fee level',
2378 95 => 'Test',
2379 96 => 'Is Pay Later',
2380 97 => 'Fee Amount',
2381 98 => 'Discount Name',
2382 99 => 'Fee Currency',
2383 100 => 'Registered By ID',
2384 101 => 'Campaign ID',
2385 ];
2386 }
2387
2388 /**
2389 * Get the definition for member headers.
2390 *
2391 * @return array
2392 */
2393 protected function getMemberHeaderDefinition() {
2394 return [
2395 81 => 'Membership Type',
2396 82 => 'Test',
2397 83 => 'Is Pay Later',
2398 84 => 'Member Since',
2399 85 => 'Membership Start Date',
2400 86 => 'Membership Expiration Date',
2401 87 => 'Source',
2402 88 => 'Membership Status',
2403 89 => 'Membership ID',
2404 90 => 'Primary Member ID',
2405 91 => 'Max Related',
2406 92 => 'Membership Recurring Contribution',
2407 93 => 'Campaign ID',
2408 94 => 'Status Override',
2409 ];
2410 }
2411
2412 /**
2413 * Get the definition for pledge headers.
2414 *
2415 * @return array
2416 */
2417 protected function getPledgeHeaderDefinition() {
2418 return [
2419 81 => 'Pledge ID',
2420 82 => 'Total Pledged',
2421 83 => 'Total Paid',
2422 84 => 'Pledge Made',
2423 85 => 'Pledge Start Date',
2424 86 => 'Next Payment Date',
2425 87 => 'Next Payment Amount',
2426 88 => 'Pledge Status',
2427 89 => 'Test',
2428 90 => 'Pledge Contribution Page Id',
2429 91 => 'pledge_financial_type',
2430 92 => 'Pledge Frequency Interval',
2431 93 => 'Pledge Frequency Unit',
2432 94 => 'pledge_currency',
2433 95 => 'Campaign ID',
2434 96 => 'Balance Amount',
2435 97 => 'Payment ID',
2436 98 => 'Scheduled Amount',
2437 99 => 'Scheduled Date',
2438 100 => 'Paid Amount',
2439 101 => 'Paid Date',
2440 102 => 'Last Reminder',
2441 103 => 'Reminders Sent',
2442 104 => 'Pledge Payment Status',
2443 ];
2444 }
2445
2446 /**
2447 * Get the column definition for exports.
2448 *
2449 * @param bool $isContactExport
2450 *
2451 * @return array
2452 */
2453 protected function getBasicSqlColumnDefinition($isContactExport) {
2454 $columns = [
2455 'civicrm_primary_id' => 'civicrm_primary_id varchar(16)',
2456 'contact_type' => 'contact_type varchar(64)',
2457 'contact_sub_type' => 'contact_sub_type varchar(255)',
2458 'do_not_email' => 'do_not_email varchar(16)',
2459 'do_not_phone' => 'do_not_phone varchar(16)',
2460 'do_not_mail' => 'do_not_mail varchar(16)',
2461 'do_not_sms' => 'do_not_sms varchar(16)',
2462 'do_not_trade' => 'do_not_trade varchar(16)',
2463 'is_opt_out' => 'is_opt_out varchar(16)',
2464 'legal_identifier' => 'legal_identifier varchar(32)',
2465 'external_identifier' => 'external_identifier varchar(64)',
2466 'sort_name' => 'sort_name varchar(128)',
2467 'display_name' => 'display_name varchar(128)',
2468 'nick_name' => 'nick_name varchar(128)',
2469 'legal_name' => 'legal_name varchar(128)',
2470 'image_url' => 'image_url longtext',
2471 'preferred_communication_method' => 'preferred_communication_method varchar(255)',
2472 'preferred_language' => 'preferred_language varchar(5)',
2473 'preferred_mail_format' => 'preferred_mail_format varchar(8)',
2474 'hash' => 'hash varchar(32)',
2475 'contact_source' => 'contact_source varchar(255)',
2476 'first_name' => 'first_name varchar(64)',
2477 'middle_name' => 'middle_name varchar(64)',
2478 'last_name' => 'last_name varchar(64)',
2479 'prefix_id' => 'prefix_id varchar(255)',
2480 'suffix_id' => 'suffix_id varchar(255)',
2481 'formal_title' => 'formal_title varchar(64)',
2482 'communication_style_id' => 'communication_style_id varchar(255)',
2483 'email_greeting_id' => 'email_greeting_id varchar(16)',
2484 'postal_greeting_id' => 'postal_greeting_id varchar(16)',
2485 'addressee_id' => 'addressee_id varchar(16)',
2486 'job_title' => 'job_title varchar(255)',
2487 'gender_id' => 'gender_id varchar(255)',
2488 'birth_date' => 'birth_date varchar(32)',
2489 'is_deceased' => 'is_deceased varchar(16)',
2490 'deceased_date' => 'deceased_date varchar(32)',
2491 'household_name' => 'household_name varchar(128)',
2492 'organization_name' => 'organization_name varchar(128)',
2493 'sic_code' => 'sic_code varchar(8)',
2494 'user_unique_id' => 'user_unique_id varchar(255)',
2495 'current_employer_id' => 'current_employer_id varchar(16)',
2496 'contact_is_deleted' => 'contact_is_deleted varchar(16)',
2497 'created_date' => 'created_date varchar(32)',
2498 'modified_date' => 'modified_date varchar(32)',
2499 'addressee' => 'addressee varchar(255)',
2500 'email_greeting' => 'email_greeting varchar(255)',
2501 'postal_greeting' => 'postal_greeting varchar(255)',
2502 'current_employer' => 'current_employer varchar(128)',
2503 'location_type' => 'location_type text',
2504 'street_address' => 'street_address varchar(96)',
2505 'street_number' => 'street_number varchar(16)',
2506 'street_number_suffix' => 'street_number_suffix varchar(8)',
2507 'street_name' => 'street_name varchar(64)',
2508 'street_unit' => 'street_unit varchar(16)',
2509 'supplemental_address_1' => 'supplemental_address_1 varchar(96)',
2510 'supplemental_address_2' => 'supplemental_address_2 varchar(96)',
2511 'supplemental_address_3' => 'supplemental_address_3 varchar(96)',
2512 'city' => 'city varchar(64)',
2513 'postal_code_suffix' => 'postal_code_suffix varchar(12)',
2514 'postal_code' => 'postal_code varchar(64)',
2515 'geo_code_1' => 'geo_code_1 varchar(32)',
2516 'geo_code_2' => 'geo_code_2 varchar(32)',
2517 'manual_geo_code' => 'manual_geo_code varchar(16)',
2518 'address_name' => 'address_name varchar(255)',
2519 'master_id' => 'master_id varchar(128)',
2520 'county' => 'county varchar(64)',
2521 'state_province' => 'state_province varchar(64)',
2522 'country' => 'country varchar(64)',
2523 'phone' => 'phone varchar(32)',
2524 'phone_ext' => 'phone_ext varchar(16)',
2525 'phone_type_id' => 'phone_type_id varchar(16)',
2526 'email' => 'email varchar(254)',
2527 'on_hold' => 'on_hold varchar(16)',
2528 'is_bulkmail' => 'is_bulkmail varchar(16)',
2529 'signature_text' => 'signature_text longtext',
2530 'signature_html' => 'signature_html longtext',
2531 'im_provider' => 'im_provider text',
2532 'im_screen_name' => 'im_screen_name varchar(64)',
2533 'openid' => 'openid varchar(255)',
2534 'world_region' => 'world_region varchar(128)',
2535 'url' => 'url varchar(128)',
2536 'groups' => 'groups text',
2537 'tags' => 'tags text',
2538 'notes' => 'notes text',
2539 ];
2540 if (!$isContactExport) {
2541 unset($columns['groups']);
2542 unset($columns['tags']);
2543 unset($columns['notes']);
2544 }
2545 return $columns;
2546 }
2547
2548 /**
2549 * Get Case SQL columns.
2550 *
2551 * @return array
2552 */
2553 protected function getCaseSqlColumns() {
2554 return [
2555 'case_start_date' => 'case_start_date varchar(32)',
2556 'case_end_date' => 'case_end_date varchar(32)',
2557 'case_subject' => 'case_subject varchar(128)',
2558 'case_source_contact_id' => 'case_source_contact_id varchar(255)',
2559 'case_activity_status' => 'case_activity_status text',
2560 'case_activity_duration' => 'case_activity_duration text',
2561 'case_activity_medium_id' => 'case_activity_medium_id varchar(255)',
2562 'case_activity_details' => 'case_activity_details text',
2563 'case_activity_is_auto' => 'case_activity_is_auto text',
2564 'contact_id' => 'contact_id varchar(16)',
2565 'case_id' => 'case_id varchar(16)',
2566 'case_activity_subject' => 'case_activity_subject text',
2567 'case_status' => 'case_status text',
2568 'case_type' => 'case_type text',
2569 'case_role' => 'case_role text',
2570 'case_deleted' => 'case_deleted varchar(16)',
2571 'case_recent_activity_date' => 'case_recent_activity_date text',
2572 'case_recent_activity_type' => 'case_recent_activity_type text',
2573 'case_scheduled_activity_date' => 'case_scheduled_activity_date text',
2574 ];
2575 }
2576
2577 /**
2578 * Get activity sql columns.
2579 *
2580 * @return array
2581 */
2582 protected function getActivitySqlColumns() {
2583 return [
2584 'activity_id' => 'activity_id varchar(16)',
2585 'activity_type' => 'activity_type varchar(255)',
2586 'activity_type_id' => 'activity_type_id varchar(16)',
2587 'activity_subject' => 'activity_subject varchar(255)',
2588 'activity_date_time' => 'activity_date_time varchar(32)',
2589 'activity_duration' => 'activity_duration varchar(16)',
2590 'activity_location' => 'activity_location varchar(255)',
2591 'activity_details' => 'activity_details longtext',
2592 'activity_status' => 'activity_status varchar(255)',
2593 'activity_priority' => 'activity_priority varchar(255)',
2594 'source_contact' => 'source_contact varchar(255)',
2595 'source_record_id' => 'source_record_id varchar(255)',
2596 'activity_is_test' => 'activity_is_test varchar(16)',
2597 'activity_campaign_id' => 'activity_campaign_id varchar(16)',
2598 'result' => 'result text',
2599 'activity_engagement_level' => 'activity_engagement_level varchar(16)',
2600 'parent_id' => 'parent_id varchar(255)',
2601 ];
2602 }
2603
2604 /**
2605 * Get participant sql columns.
2606 *
2607 * @return array
2608 */
2609 protected function getParticipantSqlColumns() {
2610 return [
2611 'event_id' => 'event_id varchar(16)',
2612 'event_title' => 'event_title varchar(255)',
2613 'event_start_date' => 'event_start_date varchar(32)',
2614 'event_end_date' => 'event_end_date varchar(32)',
2615 'event_type' => 'event_type varchar(255)',
2616 'participant_id' => 'participant_id varchar(16)',
2617 'participant_status' => 'participant_status varchar(255)',
2618 'participant_status_id' => 'participant_status_id varchar(16)',
2619 'participant_role' => 'participant_role varchar(255)',
2620 'participant_role_id' => 'participant_role_id varchar(128)',
2621 'participant_note' => 'participant_note text',
2622 'participant_register_date' => 'participant_register_date varchar(32)',
2623 'participant_source' => 'participant_source varchar(128)',
2624 'participant_fee_level' => 'participant_fee_level longtext',
2625 'participant_is_test' => 'participant_is_test varchar(16)',
2626 'participant_is_pay_later' => 'participant_is_pay_later varchar(16)',
2627 'participant_fee_amount' => 'participant_fee_amount varchar(32)',
2628 'participant_discount_name' => 'participant_discount_name varchar(16)',
2629 'participant_fee_currency' => 'participant_fee_currency varchar(3)',
2630 'participant_registered_by_id' => 'participant_registered_by_id varchar(16)',
2631 'participant_campaign_id' => 'participant_campaign_id varchar(16)',
2632 ];
2633 }
2634
2635 /**
2636 * Get contribution sql columns.
2637 *
2638 * @return array
2639 */
2640 public function getContributionSqlColumns() {
2641 return [
2642 'civicrm_primary_id' => 'civicrm_primary_id varchar(16)',
2643 'contact_type' => 'contact_type varchar(64)',
2644 'contact_sub_type' => 'contact_sub_type varchar(255)',
2645 'do_not_email' => 'do_not_email varchar(16)',
2646 'do_not_phone' => 'do_not_phone varchar(16)',
2647 'do_not_mail' => 'do_not_mail varchar(16)',
2648 'do_not_sms' => 'do_not_sms varchar(16)',
2649 'do_not_trade' => 'do_not_trade varchar(16)',
2650 'is_opt_out' => 'is_opt_out varchar(16)',
2651 'legal_identifier' => 'legal_identifier varchar(32)',
2652 'external_identifier' => 'external_identifier varchar(64)',
2653 'sort_name' => 'sort_name varchar(128)',
2654 'display_name' => 'display_name varchar(128)',
2655 'nick_name' => 'nick_name varchar(128)',
2656 'legal_name' => 'legal_name varchar(128)',
2657 'image_url' => 'image_url longtext',
2658 'preferred_communication_method' => 'preferred_communication_method varchar(255)',
2659 'preferred_language' => 'preferred_language varchar(5)',
2660 'preferred_mail_format' => 'preferred_mail_format varchar(8)',
2661 'hash' => 'hash varchar(32)',
2662 'contact_source' => 'contact_source varchar(255)',
2663 'first_name' => 'first_name varchar(64)',
2664 'middle_name' => 'middle_name varchar(64)',
2665 'last_name' => 'last_name varchar(64)',
2666 'prefix_id' => 'prefix_id varchar(255)',
2667 'suffix_id' => 'suffix_id varchar(255)',
2668 'formal_title' => 'formal_title varchar(64)',
2669 'communication_style_id' => 'communication_style_id varchar(255)',
2670 'email_greeting_id' => 'email_greeting_id varchar(16)',
2671 'postal_greeting_id' => 'postal_greeting_id varchar(16)',
2672 'addressee_id' => 'addressee_id varchar(16)',
2673 'job_title' => 'job_title varchar(255)',
2674 'gender_id' => 'gender_id varchar(255)',
2675 'birth_date' => 'birth_date varchar(32)',
2676 'is_deceased' => 'is_deceased varchar(16)',
2677 'deceased_date' => 'deceased_date varchar(32)',
2678 'household_name' => 'household_name varchar(128)',
2679 'organization_name' => 'organization_name varchar(128)',
2680 'sic_code' => 'sic_code varchar(8)',
2681 'user_unique_id' => 'user_unique_id varchar(255)',
2682 'current_employer_id' => 'current_employer_id varchar(16)',
2683 'contact_is_deleted' => 'contact_is_deleted varchar(16)',
2684 'created_date' => 'created_date varchar(32)',
2685 'modified_date' => 'modified_date varchar(32)',
2686 'addressee' => 'addressee varchar(255)',
2687 'email_greeting' => 'email_greeting varchar(255)',
2688 'postal_greeting' => 'postal_greeting varchar(255)',
2689 'current_employer' => 'current_employer varchar(128)',
2690 'location_type' => 'location_type text',
2691 'street_address' => 'street_address varchar(96)',
2692 'street_number' => 'street_number varchar(16)',
2693 'street_number_suffix' => 'street_number_suffix varchar(8)',
2694 'street_name' => 'street_name varchar(64)',
2695 'street_unit' => 'street_unit varchar(16)',
2696 'supplemental_address_1' => 'supplemental_address_1 varchar(96)',
2697 'supplemental_address_2' => 'supplemental_address_2 varchar(96)',
2698 'supplemental_address_3' => 'supplemental_address_3 varchar(96)',
2699 'city' => 'city varchar(64)',
2700 'postal_code_suffix' => 'postal_code_suffix varchar(12)',
2701 'postal_code' => 'postal_code varchar(64)',
2702 'geo_code_1' => 'geo_code_1 varchar(32)',
2703 'geo_code_2' => 'geo_code_2 varchar(32)',
2704 'address_name' => 'address_name varchar(255)',
2705 'master_id' => 'master_id varchar(128)',
2706 'county' => 'county varchar(64)',
2707 'state_province' => 'state_province varchar(64)',
2708 'country' => 'country varchar(64)',
2709 'phone' => 'phone varchar(32)',
2710 'phone_ext' => 'phone_ext varchar(16)',
2711 'email' => 'email varchar(254)',
2712 'on_hold' => 'on_hold varchar(16)',
2713 'is_bulkmail' => 'is_bulkmail varchar(16)',
2714 'signature_text' => 'signature_text longtext',
2715 'signature_html' => 'signature_html longtext',
2716 'im_provider' => 'im_provider text',
2717 'im_screen_name' => 'im_screen_name varchar(64)',
2718 'openid' => 'openid varchar(255)',
2719 'world_region' => 'world_region varchar(128)',
2720 'url' => 'url varchar(128)',
2721 'phone_type_id' => 'phone_type_id varchar(16)',
2722 'financial_type' => 'financial_type varchar(255)',
2723 'contribution_source' => 'contribution_source varchar(255)',
2724 'receive_date' => 'receive_date varchar(32)',
2725 'thankyou_date' => 'thankyou_date varchar(32)',
2726 'contribution_cancel_date' => 'contribution_cancel_date varchar(32)',
2727 'total_amount' => 'total_amount varchar(32)',
2728 'accounting_code' => 'accounting_code varchar(64)',
2729 'payment_instrument' => 'payment_instrument varchar(255)',
2730 'payment_instrument_id' => 'payment_instrument_id varchar(16)',
2731 'contribution_check_number' => 'contribution_check_number varchar(255)',
2732 'non_deductible_amount' => 'non_deductible_amount varchar(32)',
2733 'fee_amount' => 'fee_amount varchar(32)',
2734 'net_amount' => 'net_amount varchar(32)',
2735 'trxn_id' => 'trxn_id varchar(255)',
2736 'invoice_id' => 'invoice_id varchar(255)',
2737 'invoice_number' => 'invoice_number varchar(255)',
2738 'currency' => 'currency varchar(3)',
2739 'cancel_reason' => 'cancel_reason longtext',
2740 'receipt_date' => 'receipt_date varchar(32)',
2741 'is_test' => 'is_test varchar(16)',
2742 'is_pay_later' => 'is_pay_later varchar(16)',
2743 'contribution_status' => 'contribution_status varchar(255)',
2744 'contribution_recur_id' => 'contribution_recur_id varchar(16)',
2745 'amount_level' => 'amount_level longtext',
2746 'contribution_note' => 'contribution_note text',
2747 'contribution_batch' => 'contribution_batch text',
2748 'contribution_campaign_title' => 'contribution_campaign_title varchar(255)',
2749 'contribution_campaign_id' => 'contribution_campaign_id varchar(16)',
2750 'contribution_soft_credit_name' => 'contribution_soft_credit_name varchar(255)',
2751 'contribution_soft_credit_amount' => 'contribution_soft_credit_amount varchar(255)',
2752 'contribution_soft_credit_type' => 'contribution_soft_credit_type varchar(255)',
2753 'contribution_soft_credit_contact_id' => 'contribution_soft_credit_contact_id varchar(255)',
2754 'contribution_soft_credit_contribution_id' => 'contribution_soft_credit_contribution_id varchar(255)',
2755 ];
2756 }
2757
2758 /**
2759 * Get pledge sql columns.
2760 *
2761 * @return array
2762 */
2763 public function getPledgeSqlColumns() {
2764 return [
2765 'pledge_id' => 'pledge_id varchar(16)',
2766 'pledge_amount' => 'pledge_amount varchar(32)',
2767 'pledge_total_paid' => 'pledge_total_paid text',
2768 'pledge_create_date' => 'pledge_create_date varchar(32)',
2769 'pledge_start_date' => 'pledge_start_date varchar(32)',
2770 'pledge_next_pay_date' => 'pledge_next_pay_date text',
2771 'pledge_next_pay_amount' => 'pledge_next_pay_amount text',
2772 'pledge_status' => 'pledge_status varchar(255)',
2773 'pledge_is_test' => 'pledge_is_test varchar(16)',
2774 'pledge_contribution_page_id' => 'pledge_contribution_page_id varchar(255)',
2775 'pledge_financial_type' => 'pledge_financial_type text',
2776 'pledge_frequency_interval' => 'pledge_frequency_interval varchar(255)',
2777 'pledge_frequency_unit' => 'pledge_frequency_unit varchar(255)',
2778 'pledge_currency' => 'pledge_currency text',
2779 'pledge_campaign_id' => 'pledge_campaign_id varchar(16)',
2780 'pledge_balance_amount' => 'pledge_balance_amount text',
2781 'pledge_payment_id' => 'pledge_payment_id varchar(16)',
2782 'pledge_payment_scheduled_amount' => 'pledge_payment_scheduled_amount varchar(32)',
2783 'pledge_payment_scheduled_date' => 'pledge_payment_scheduled_date varchar(32)',
2784 'pledge_payment_paid_amount' => 'pledge_payment_paid_amount text',
2785 'pledge_payment_paid_date' => 'pledge_payment_paid_date text',
2786 'pledge_payment_reminder_date' => 'pledge_payment_reminder_date varchar(32)',
2787 'pledge_payment_reminder_count' => 'pledge_payment_reminder_count varchar(16)',
2788 'pledge_payment_status' => 'pledge_payment_status varchar(255)',
2789 ];
2790 }
2791
2792 /**
2793 * Get membership sql columns.
2794 *
2795 * @return array
2796 */
2797 public function getMembershipSqlColumns() {
2798 return [
2799 'membership_type' => 'membership_type varchar(128)',
2800 'member_is_test' => 'member_is_test varchar(16)',
2801 'member_is_pay_later' => 'member_is_pay_later varchar(16)',
2802 'membership_join_date' => 'membership_join_date varchar(32)',
2803 'membership_start_date' => 'membership_start_date varchar(32)',
2804 'membership_end_date' => 'membership_end_date varchar(32)',
2805 'membership_source' => 'membership_source varchar(128)',
2806 'membership_status' => 'membership_status varchar(255)',
2807 'membership_id' => 'membership_id varchar(16)',
2808 'owner_membership_id' => 'owner_membership_id varchar(16)',
2809 'max_related' => 'max_related varchar(16)',
2810 'membership_recur_id' => 'membership_recur_id varchar(16)',
2811 'member_campaign_id' => 'member_campaign_id varchar(16)',
2812 'member_is_override' => 'member_is_override varchar(16)',
2813 ];
2814 }
2815
2816 /**
2817 * Change our location types so we have some edge cases in the mix.
2818 *
2819 * - a space in the name
2820 * - name differs from label
2821 * - non-anglo char in the label (not valid in the name).
2822 */
2823 protected function diversifyLocationTypes() {
2824 $this->locationTypes['Main'] = $this->callAPISuccess('Location_type', 'get', [
2825 'name' => 'Main',
2826 'return' => 'id',
2827 'api.LocationType.Create' => ['display_name' => 'Méin'],
2828 ]);
2829 $this->locationTypes['Whare Kai'] = $this->callAPISuccess('Location_type', 'create', [
2830 'name' => 'Whare Kai',
2831 'display_name' => 'Whare Kai',
2832 ]);
2833 }
2834
2835 /**
2836 * Test export components.
2837 *
2838 * Tests the exportComponents function with the provided parameters.
2839 *
2840 * This exportComponents will export a csv but it will also throw a prematureExitException
2841 * which we catch & grab the processor from.
2842 *
2843 * $this->processor is set to the export processor.
2844 *
2845 * @param $params
2846 *
2847 * @throws \CRM_Core_Exception
2848 * @throws \League\Csv\Exception
2849 */
2850 protected function doExportTest($params) {
2851 $this->startCapturingOutput();
2852 try {
2853 $exportMode = CRM_Utils_Array::value('exportMode', $params, CRM_Export_Form_Select::CONTACT_EXPORT);
2854 $ids = CRM_Utils_Array::value('ids', $params, ($exportMode === CRM_Export_Form_Select::CONTACT_EXPORT ? $this->contactIDs : []));
2855 $defaultClause = (empty($ids) ? NULL : "contact_a.id IN (" . implode(',', $ids) . ")");
2856 CRM_Export_BAO_Export::exportComponents(
2857 CRM_Utils_Array::value('selectAll', $params, (empty($params['fields']))),
2858 $ids,
2859 CRM_Utils_Array::value('params', $params, []),
2860 CRM_Utils_Array::value('order', $params),
2861 CRM_Utils_Array::value('fields', $params, []),
2862 CRM_Utils_Array::value('moreReturnProperties', $params),
2863 $exportMode,
2864 CRM_Utils_Array::value('componentClause', $params, $defaultClause),
2865 CRM_Utils_Array::value('componentTable', $params),
2866 CRM_Utils_Array::value('mergeSameAddress', $params, FALSE),
2867 CRM_Utils_Array::value('mergeSameHousehold', $params, FALSE),
2868 CRM_Utils_Array::value('exportParams', $params, [])
2869 );
2870 }
2871 catch (CRM_Core_Exception_PrematureExitException $e) {
2872 $this->processor = $e->errorData['processor'];
2873 $this->csv = $this->captureOutputToCSV();
2874 return;
2875 }
2876 $this->fail('We expected a premature exit exception');
2877 }
2878
2879 /**
2880 * Assert that the received array matches the expected, ignoring time sensitive fields.
2881 *
2882 * @param array $expected
2883 * @param array $row
2884 */
2885 protected function assertExpectedOutput(array $expected, array $row) {
2886 $variableFields = ['Created Date', 'Modified Date', 'Contact Hash'];
2887 foreach ($expected as $key => $value) {
2888 if (in_array($key, $variableFields)) {
2889 $this->assertTrue(!empty($row[$key]));
2890 }
2891 else {
2892 $this->assertEquals($value, $row[$key]);
2893 }
2894 }
2895 }
2896
2897 /**
2898 * Test get preview function on export processor.
2899 *
2900 * @throws \CRM_Core_Exception
2901 */
2902 public function testExportGetPreview() {
2903 $this->setUpContactExportData();
2904 $fields = [
2905 ['Individual', 'first_name'],
2906 ['Individual', 'last_name'],
2907 ['Individual', 'street_address', 1],
2908 ['Individual', 'city', 1],
2909 ['Individual', 'country', 1],
2910 ['Individual', 'email', 1],
2911 ];
2912 $mappedFields = [];
2913 foreach ($fields as $field) {
2914 $mappedFields[] = CRM_Core_BAO_Mapping::getMappingParams([], $field);
2915 }
2916 $processor = new CRM_Export_BAO_ExportProcessor(FALSE, $mappedFields,
2917 'AND');
2918 $processor->setComponentClause('contact_a.id IN (' . implode(',', $this->contactIDs) . ')');
2919 $result = $processor->getPreview(2);
2920 $this->assertEquals([
2921 [
2922 'first_name' => 'Anthony',
2923 'last_name' => 'Anderson',
2924 'Home-street_address' => 'Ambachtstraat 23',
2925 'Home-city' => 'Brummen',
2926 'Home-country' => 'Netherlands',
2927 'Home-email' => 'home@example.com',
2928 ],
2929 [
2930 'first_name' => 'Anthony',
2931 'last_name' => 'Anderson',
2932 'Home-street_address' => '',
2933 'Home-city' => '',
2934 'Home-country' => '',
2935 'Home-email' => 'anthony_anderson@civicrm.org',
2936 ],
2937 ], $result);
2938 }
2939
2940 /**
2941 * Set up contacts which will be merged with the same address option.
2942 *
2943 * @throws \CRM_Core_Exception
2944 */
2945 protected function setUpContactSameAddressExportData() {
2946 $this->setUpContactExportData();
2947 $this->contactIDs[] = $contact3 = $this->individualCreate(['first_name' => 'Sarah', 'last_name' => 'Smith', 'prefix_id' => 'Dr.']);
2948 // Create address for contact A.
2949 $params = [
2950 'contact_id' => $contact3,
2951 'location_type_id' => 'Home',
2952 'street_address' => 'Ambachtstraat 23',
2953 'postal_code' => '6971 BN',
2954 'country_id' => '1152',
2955 'city' => 'Brummen',
2956 'is_primary' => 1,
2957 ];
2958 $this->callAPISuccess('address', 'create', $params);
2959 }
2960
2961 }