3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
12 use Civi\Api4\Activity
;
13 use Civi\Api4\ActivityContact
;
16 * Class CRM_Core_BAO_ActionScheduleTest
17 * @group ActionSchedule
20 * There are additional tests for some specific entities in other classes:
21 * @see CRM_Activity_ActionMappingTest
22 * @see CRM_Contribute_ActionMapping_ByTypeTest
24 class CRM_Core_BAO_ActionScheduleTest
extends CiviUnitTestCase
{
26 use CRMTraits_Custom_CustomDataTrait
;
34 * Entities set up for the test.
38 private $fixtures = [];
43 * @throws CRM_Core_Exception
45 public function setUp(): void
{
48 $this->mut
= new CiviMailUtils($this, TRUE);
50 $this->fixtures
['rolling_membership_type'] = [
51 'period_type' => 'rolling',
52 'duration_unit' => 'month',
53 'duration_interval' => '3',
56 'financial_type_id' => 2,
59 $this->fixtures
['rolling_membership'] = [
60 'membership_type_id' => [
61 'period_type' => 'rolling',
62 'duration_unit' => 'month',
63 'duration_interval' => '3',
66 'join_date' => '20120315',
67 'start_date' => '20120315',
68 'end_date' => '20120615',
72 $this->fixtures
['rolling_membership_past'] = [
73 'membership_type_id' => [
74 'period_type' => 'rolling',
75 'duration_unit' => 'month',
76 'duration_interval' => '3',
79 'join_date' => '20100310',
80 'start_date' => '20100310',
81 'end_date' => '20100610',
82 'is_override' => 'NULL',
84 $this->fixtures
['participant'] = [
88 'title' => 'Example Event',
89 'start_date' => '20120315',
90 'end_date' => '20120615',
98 $this->fixtures
['phone_call'] = [
100 'activity_type_id' => 2,
101 'activity_date_time' => '20120615100000',
102 'is_current_revision' => 1,
105 $this->fixtures
['contact'] = [
107 'contact_type' => 'Individual',
108 'email' => 'test-member@example.com',
109 'gender_id' => 'Female',
110 'first_name' => 'Churmondleia',
111 'last_name' => 'Ōtākou',
113 $this->fixtures
['contact_2'] = [
115 'contact_type' => 'Individual',
116 'email' => 'test-contact-2@example.com',
117 'gender_id' => 'Male',
118 'first_name' => 'Fabio',
121 $this->fixtures
['contact_birthdate'] = [
123 'contact_type' => 'Individual',
124 'email' => 'test-birth_day@example.com',
125 'birth_date' => '20050707',
127 $this->fixtures
['sched_activity_1day'] = [
128 'name' => 'One_Day_Phone_Call_Notice',
129 'title' => 'One Day Phone Call Notice',
131 'absolute_date' => NULL,
132 'body_html' => '<p>1-Day (non-repeating) (for {activity.subject})</p>',
133 'body_text' => '1-Day (non-repeating) (for {activity.subject})',
134 'end_action' => NULL,
136 'end_frequency_interval' => NULL,
137 'end_frequency_unit' => NULL,
138 'entity_status' => '1',
139 'entity_value' => '2',
144 'msg_template_id' => NULL,
146 'recipient_listing' => NULL,
147 'recipient_manual' => NULL,
148 'record_activity' => 1,
149 'repetition_frequency_interval' => NULL,
150 'repetition_frequency_unit' => NULL,
151 'start_action_condition' => 'before',
152 'start_action_date' => 'activity_date_time',
153 'start_action_offset' => '1',
154 'start_action_unit' => 'day',
155 'subject' => '1-Day (non-repeating) (about {activity.activity_type})',
157 $this->fixtures
['sched_activity_1day_r'] = [
158 'name' => 'One_Day_Phone_Call_Notice_R',
159 'title' => 'One Day Phone Call Notice R',
161 'absolute_date' => NULL,
162 'body_html' => '<p>1-Day (repeating)</p>',
163 'body_text' => '1-Day (repeating)',
164 'end_action' => 'after',
165 'end_date' => 'activity_date_time',
166 'end_frequency_interval' => '2',
167 'end_frequency_unit' => 'day',
168 'entity_status' => '1',
169 'entity_value' => '2',
174 'msg_template_id' => NULL,
176 'recipient_listing' => NULL,
177 'recipient_manual' => NULL,
178 'record_activity' => NULL,
179 'repetition_frequency_interval' => '6',
180 'repetition_frequency_unit' => 'hour',
181 'start_action_condition' => 'before',
182 'start_action_date' => 'activity_date_time',
183 'start_action_offset' => '1',
184 'start_action_unit' => 'day',
185 'subject' => '1-Day (repeating) (about {activity.activity_type})',
187 $this->fixtures
['sched_activity_1day_r_on_abs_date'] = [
188 'name' => 'One_Day_Phone_Call_Notice_R',
189 'title' => 'One Day Phone Call Notice R',
191 'absolute_date' => CRM_Utils_Date
::processDate('20120614100000'),
192 'body_html' => '<p>1-Day (repeating)</p>',
193 'body_text' => '1-Day (repeating)',
194 'entity_status' => '1',
195 'entity_value' => '2',
200 'msg_template_id' => NULL,
202 'recipient_listing' => NULL,
203 'recipient_manual' => NULL,
204 'record_activity' => NULL,
205 'repetition_frequency_interval' => '6',
206 'repetition_frequency_unit' => 'hour',
207 'end_action' => 'after',
208 'end_date' => 'activity_date_time',
209 'end_frequency_interval' => '2',
210 'end_frequency_unit' => 'day',
211 'start_action_condition' => '',
212 'start_action_date' => '',
213 'start_action_offset' => '',
214 'start_action_unit' => '',
215 'subject' => '1-Day (repeating) (about {activity.activity_type})',
217 $this->fixtures
['sched_event_name_1day_on_abs_date'] = [
218 'name' => 'sched_event_name_1day_on_abs_date',
219 'title' => 'sched_event_name_1day_on_abs_date',
221 'absolute_date' => CRM_Utils_Date
::processDate('20120614100000'),
222 'body_html' => '<p>sched_event_name_1day_on_abs_date</p>',
223 'body_text' => 'sched_event_name_1day_on_abs_date',
224 'entity_status' => '1',
225 'entity_value' => '2',
230 'msg_template_id' => NULL,
232 'recipient_listing' => NULL,
233 'recipient_manual' => NULL,
234 'record_activity' => NULL,
235 'repetition_frequency_interval' => NULL,
236 'repetition_frequency_unit' => NULL,
237 'end_action' => NULL,
239 'end_frequency_interval' => NULL,
240 'end_frequency_unit' => NULL,
241 'start_action_condition' => NULL,
242 'start_action_date' => NULL,
243 'start_action_offset' => NULL,
244 'start_action_unit' => NULL,
245 'subject' => 'sched_event_name_1day_on_abs_date',
247 $this->fixtures
['sched_membership_join_2week'] = [
248 'name' => 'sched_membership_join_2week',
249 'title' => 'sched_membership_join_2week',
250 'absolute_date' => '',
251 'body_html' => '<p>body sched_membership_join_2week</p>',
252 'body_text' => 'body sched_membership_join_2week',
255 'end_frequency_interval' => '',
256 'end_frequency_unit' => '',
257 'entity_status' => '',
258 'entity_value' => '',
263 'msg_template_id' => '',
265 'recipient_listing' => '',
266 'recipient_manual' => '',
267 'record_activity' => 1,
268 'repetition_frequency_interval' => '',
269 'repetition_frequency_unit' => '',
270 'start_action_condition' => 'after',
271 'start_action_date' => 'membership_join_date',
272 'start_action_offset' => '2',
273 'start_action_unit' => 'week',
274 'subject' => 'subject sched_membership_join_2week (joined {membership.join_date})',
276 $this->fixtures
['sched_membership_start_1week'] = [
277 'name' => 'sched_membership_start_1week',
278 'title' => 'sched_membership_start_1week',
279 'absolute_date' => '',
280 'body_html' => '<p>body sched_membership_start_1week</p>',
281 'body_text' => 'body sched_membership_start_1week',
284 'end_frequency_interval' => '',
285 'end_frequency_unit' => '',
286 'entity_status' => '',
287 'entity_value' => '',
292 'msg_template_id' => '',
294 'recipient_listing' => '',
295 'recipient_manual' => '',
296 'record_activity' => 1,
297 'repetition_frequency_interval' => '',
298 'repetition_frequency_unit' => '',
299 'start_action_condition' => 'after',
300 'start_action_date' => 'membership_start_date',
301 'start_action_offset' => '1',
302 'start_action_unit' => 'week',
303 'subject' => 'subject sched_membership_start_1week (joined {membership.start_date})',
305 $this->fixtures
['sched_membership_end_2week'] = [
306 'name' => 'sched_membership_end_2week',
307 'title' => 'sched_membership_end_2week',
308 'absolute_date' => '',
309 'body_html' => '<p>body sched_membership_end_2week</p>',
310 'body_text' => 'body sched_membership_end_2week',
313 'end_frequency_interval' => '',
314 'end_frequency_unit' => '',
315 'entity_status' => '',
316 'entity_value' => '',
321 'msg_template_id' => '',
323 'recipient_listing' => '',
324 'recipient_manual' => '',
325 'record_activity' => 1,
326 'repetition_frequency_interval' => '',
327 'repetition_frequency_unit' => '',
328 'start_action_condition' => 'before',
329 'start_action_date' => 'membership_end_date',
330 'start_action_offset' => '2',
331 'start_action_unit' => 'week',
332 'subject' => 'subject sched_membership_end_2week',
334 $this->fixtures
['sched_on_membership_end_date'] = [
335 'name' => 'sched_on_membership_end_date',
336 'title' => 'sched_on_membership_end_date',
337 'body_html' => '<p>Your membership expired today</p>',
338 'body_text' => 'Your membership expired today',
341 'record_activity' => 1,
342 'start_action_condition' => 'after',
343 'start_action_date' => 'membership_end_date',
344 'start_action_offset' => '0',
345 'start_action_unit' => 'hour',
346 'subject' => 'subject send reminder on membership_end_date',
348 $this->fixtures
['sched_after_1day_membership_end_date'] = [
349 'name' => 'sched_after_1day_membership_end_date',
350 'title' => 'sched_after_1day_membership_end_date',
351 'body_html' => '<p>Your membership expired yesterday</p>',
352 'body_text' => 'Your membership expired yesterday',
355 'record_activity' => 1,
356 'start_action_condition' => 'after',
357 'start_action_date' => 'membership_end_date',
358 'start_action_offset' => '1',
359 'start_action_unit' => 'day',
360 'subject' => 'subject send reminder on membership_end_date',
363 $this->fixtures
['sched_membership_end_2month'] = [
364 'name' => 'sched_membership_end_2month',
365 'title' => 'sched_membership_end_2month',
366 'absolute_date' => '',
367 'body_html' => '<p>body sched_membership_end_2month</p>',
368 'body_text' => 'body sched_membership_end_2month',
371 'end_frequency_interval' => '',
372 'end_frequency_unit' => '',
373 'entity_status' => '',
374 'entity_value' => '',
379 'msg_template_id' => '',
381 'recipient_listing' => '',
382 'recipient_manual' => '',
383 'record_activity' => 1,
384 'repetition_frequency_interval' => '',
385 'repetition_frequency_unit' => '',
386 'start_action_condition' => 'after',
387 'start_action_date' => 'membership_end_date',
388 'start_action_offset' => '2',
389 'start_action_unit' => 'month',
390 'subject' => 'subject sched_membership_end_2month',
393 $this->fixtures
['sched_membership_absolute_date'] = [
394 'name' => 'sched_membership_absolute_date',
395 'title' => 'sched_membership_absolute_date',
396 'absolute_date' => CRM_Utils_Date
::processDate('20120614100000'),
397 'body_html' => '<p>body sched_membership_absolute_date</p>',
398 'body_text' => 'body sched_membership_absolute_date',
401 'end_frequency_interval' => '',
402 'end_frequency_unit' => '',
403 'entity_status' => '',
404 'entity_value' => '',
409 'msg_template_id' => '',
411 'recipient_listing' => '',
412 'recipient_manual' => '',
413 'record_activity' => 1,
414 'repetition_frequency_interval' => '',
415 'repetition_frequency_unit' => '',
416 'start_action_condition' => '',
417 'start_action_date' => '',
418 'start_action_offset' => '',
419 'start_action_unit' => '',
420 'subject' => 'subject sched_membership_absolute_date',
423 $this->fixtures
['sched_contact_birth_day_yesterday'] = [
424 'name' => 'sched_contact_birth_day_yesterday',
425 'title' => 'sched_contact_birth_day_yesterday',
426 'absolute_date' => '',
427 'body_html' => '<p>you look like you were born yesterday!</p>',
428 'body_text' => 'you look like you were born yesterday!',
431 'end_frequency_interval' => '',
432 'end_frequency_unit' => '',
433 'entity_status' => 1,
434 'entity_value' => 'birth_date',
439 'msg_template_id' => '',
441 'recipient_listing' => '',
442 'recipient_manual' => '',
443 'record_activity' => 1,
444 'repetition_frequency_interval' => '',
445 'repetition_frequency_unit' => '',
446 'start_action_condition' => 'after',
447 'start_action_date' => 'date_field',
448 'start_action_offset' => '1',
449 'start_action_unit' => 'day',
450 'subject' => 'subject sched_contact_birth_day_yesterday',
453 $this->fixtures
['sched_contact_birth_day_anniversary'] = [
454 'name' => 'sched_contact_birth_day_anniversary',
455 'title' => 'sched_contact_birth_day_anniversary',
456 'absolute_date' => '',
457 'body_html' => '<p>happy birthday!</p>',
458 'body_text' => 'happy birthday!',
461 'end_frequency_interval' => '',
462 'end_frequency_unit' => '',
463 'entity_status' => 2,
464 'entity_value' => 'birth_date',
469 'msg_template_id' => '',
471 'recipient_listing' => '',
472 'recipient_manual' => '',
473 'record_activity' => 1,
474 'repetition_frequency_interval' => '',
475 'repetition_frequency_unit' => '',
476 'start_action_condition' => 'before',
477 'start_action_date' => 'date_field',
478 'start_action_offset' => '1',
479 'start_action_unit' => 'day',
480 'subject' => 'subject sched_contact_birth_day_anniversary',
483 $this->fixtures
['sched_contact_grad_tomorrow'] = [
484 'name' => 'sched_contact_grad_tomorrow',
485 'title' => 'sched_contact_grad_tomorrow',
486 'absolute_date' => '',
487 'body_html' => '<p>congratulations on your graduation!</p>',
488 'body_text' => 'congratulations on your graduation!',
491 'end_frequency_interval' => '',
492 'end_frequency_unit' => '',
493 'entity_status' => 1,
498 'msg_template_id' => '',
500 'recipient_listing' => '',
501 'recipient_manual' => '',
502 'record_activity' => 1,
503 'repetition_frequency_interval' => '',
504 'repetition_frequency_unit' => '',
505 'start_action_condition' => 'before',
506 'start_action_date' => 'date_field',
507 'start_action_offset' => '1',
508 'start_action_unit' => 'day',
509 'subject' => 'subject sched_contact_grad_tomorrow',
512 $this->fixtures
['sched_contact_grad_anniversary'] = [
513 'name' => 'sched_contact_grad_anniversary',
514 'title' => 'sched_contact_grad_anniversary',
515 'absolute_date' => '',
516 'body_html' => '<p>dear alum, please send us money.</p>',
517 'body_text' => 'dear alum, please send us money.',
520 'end_frequency_interval' => '',
521 'end_frequency_unit' => '',
522 'entity_status' => 2,
527 'msg_template_id' => '',
529 'recipient_listing' => '',
530 'recipient_manual' => '',
531 'record_activity' => 1,
532 'repetition_frequency_interval' => '',
533 'repetition_frequency_unit' => '',
534 'start_action_condition' => 'after',
535 'start_action_date' => 'date_field',
536 'start_action_offset' => '1',
537 'start_action_unit' => 'week',
538 'subject' => 'subject sched_contact_grad_anniversary',
541 $this->fixtures
['sched_contact_created_yesterday'] = [
542 'name' => 'sched_contact_created_yesterday',
543 'title' => 'sched_contact_created_yesterday',
544 'absolute_date' => '',
545 'body_html' => '<p>Your contact was created yesterday</p>',
546 'body_text' => 'Your contact was created yesterday!',
549 'end_frequency_interval' => '',
550 'end_frequency_unit' => '',
551 'entity_status' => 1,
552 'entity_value' => 'created_date',
557 'msg_template_id' => '',
559 'recipient_listing' => '',
560 'recipient_manual' => '',
561 'record_activity' => 1,
562 'repetition_frequency_interval' => '',
563 'repetition_frequency_unit' => '',
564 'start_action_condition' => 'after',
565 'start_action_date' => 'date_field',
566 'start_action_offset' => '1',
567 'start_action_unit' => 'day',
568 'subject' => 'subject sched_contact_created_yesterday',
571 $this->fixtures
['sched_contact_mod_anniversary'] = [
572 'name' => 'sched_contact_mod_anniversary',
573 'title' => 'sched_contact_mod_anniversary',
574 'absolute_date' => '',
575 'body_html' => '<p>You last updated your data last year</p>',
576 'body_text' => 'Go update your stuff!',
579 'end_frequency_interval' => '',
580 'end_frequency_unit' => '',
581 'entity_status' => 2,
582 'entity_value' => 'modified_date',
587 'msg_template_id' => '',
589 'recipient_listing' => '',
590 'recipient_manual' => '',
591 'record_activity' => 1,
592 'repetition_frequency_interval' => '',
593 'repetition_frequency_unit' => '',
594 'start_action_condition' => 'before',
595 'start_action_date' => 'date_field',
596 'start_action_offset' => '1',
597 'start_action_unit' => 'day',
598 'subject' => 'subject sched_contact_mod_anniversary',
601 $this->fixtures
['sched_event_type_start_1week_before'] = [
602 'name' => 'sched_event_type_start_1week_before',
603 'title' => 'sched_event_type_start_1week_before',
604 'absolute_date' => '',
605 'body_html' => '<p>body sched_event_type_start_1week_before ({event.title})</p>',
606 'body_text' => 'body sched_event_type_start_1week_before ({event.title})',
609 'end_frequency_interval' => '',
610 'end_frequency_unit' => '',
611 // participant status id
612 'entity_status' => '',
614 'entity_value' => '',
620 'msg_template_id' => '',
622 'recipient_listing' => '',
623 'recipient_manual' => '',
624 'record_activity' => 1,
625 'repetition_frequency_interval' => '',
626 'repetition_frequency_unit' => '',
627 'start_action_condition' => 'before',
628 'start_action_date' => 'event_start_date',
629 'start_action_offset' => '1',
630 'start_action_unit' => 'week',
631 'subject' => 'subject sched_event_type_start_1week_before ({event.title})',
633 $this->fixtures
['sched_event_type_end_2month_repeat_twice_2_weeks'] = [
634 'name' => 'sched_event_type_end_2month_repeat_twice_2_weeks',
635 'title' => 'sched_event_type_end_2month_repeat_twice_2_weeks',
636 'absolute_date' => '',
637 'body_html' => '<p>body sched_event_type_end_2month_repeat_twice_2_weeks {event.title}</p>',
638 'body_text' => 'body sched_event_type_end_2month_repeat_twice_2_weeks {event.title}',
639 'end_action' => 'after',
640 'end_date' => 'event_end_date',
641 'end_frequency_interval' => '3',
642 'end_frequency_unit' => 'month',
643 // participant status id
644 'entity_status' => '',
646 'entity_value' => '',
652 'msg_template_id' => '',
654 'recipient_listing' => '',
655 'recipient_manual' => '',
656 'record_activity' => 1,
657 'repetition_frequency_interval' => '2',
658 'repetition_frequency_unit' => 'week',
659 'start_action_condition' => 'after',
660 'start_action_date' => 'event_end_date',
661 'start_action_offset' => '2',
662 'start_action_unit' => 'month',
663 'subject' => 'subject sched_event_type_end_2month_repeat_twice_2_weeks {event.title}',
666 $this->fixtures
['sched_membership_end_2month_repeat_twice_4_weeks'] = [
667 'name' => 'sched_membership_end_2month',
668 'title' => 'sched_membership_end_2month',
669 'absolute_date' => '',
670 'body_html' => '<p>body sched_membership_end_2month</p>',
671 'body_text' => 'body sched_membership_end_2month',
673 'end_date' => 'membership_end_date',
674 'end_frequency_interval' => '4',
675 'end_frequency_unit' => 'month',
676 'entity_status' => '',
677 'entity_value' => '',
682 'msg_template_id' => '',
684 'recipient_listing' => '',
685 'recipient_manual' => '',
686 'record_activity' => 1,
687 'repetition_frequency_interval' => '4',
688 'repetition_frequency_unit' => 'week',
689 'start_action_condition' => 'after',
690 'start_action_date' => 'membership_end_date',
691 'start_action_offset' => '2',
692 'start_action_unit' => 'month',
693 'subject' => 'subject sched_membership_end_2month',
695 $this->fixtures
['sched_membership_end_limit_to_none'] = [
696 'name' => 'limit to none',
697 'title' => 'limit to none',
698 'absolute_date' => '',
699 'body_html' => '<p>body sched_membership_end_2month</p>',
700 'body_text' => 'body sched_membership_end_2month',
703 'end_frequency_interval' => '4',
704 'end_frequency_unit' => 'month',
705 'entity_status' => '',
706 'entity_value' => '',
712 'msg_template_id' => '',
714 'recipient_listing' => '',
715 'recipient_manual' => '',
716 'record_activity' => 1,
717 'repetition_frequency_interval' => '4',
718 'repetition_frequency_unit' => 'week',
719 'start_action_condition' => 'after',
720 'start_action_date' => 'membership_end_date',
721 'start_action_offset' => '2',
722 'start_action_unit' => 'month',
723 'subject' => 'limit to none',
725 $this->fixtures
['sched_on_membership_end_date_repeat_interval'] = [
726 'name' => 'sched_on_membership_end_date',
727 'title' => 'sched_on_membership_end_date',
728 'body_html' => '<p>Your membership expired 1 unit ago</p>',
729 'body_text' => 'Your membership expired 1 unit ago',
730 'end_frequency_interval' => 10,
731 'end_frequency_unit' => 'year',
735 'record_activity' => 1,
736 'start_action_condition' => 'after',
737 'start_action_date' => 'membership_end_date',
738 'start_action_offset' => '0',
739 'start_action_unit' => 'hour',
740 'subject' => 'subject send reminder every unit after membership_end_date',
743 $customGroup = $this->callAPISuccess('CustomGroup', 'create', [
744 'title' => ts('Test Contact Custom group'),
745 'name' => 'test_contact_cg',
746 'extends' => 'Contact',
747 'domain_id' => CRM_Core_Config
::domainID(),
749 'collapse_adv_display' => 0,
750 'collapse_display' => 0,
752 $customField = $this->callAPISuccess('CustomField', 'create', [
753 'label' => 'Test Text',
754 'data_type' => 'String',
755 'html_type' => 'Text',
756 'custom_group_id' => $customGroup['id'],
758 $this->fixtures
['contact_custom_token'] = [
759 'id' => $customField['id'],
760 'token' => sprintf('{contact.custom_%s}', $customField['id']),
761 'name' => sprintf('custom_%s', $customField['id']),
762 'value' => 'text ' . substr(sha1(mt_rand()), 0, 7),
769 * Tears down the fixture, for example, closes a network connection.
771 * This method is called after a test is executed.
773 * @throws \CRM_Core_Exception
775 public function tearDown(): void
{
777 $this->mut
->clearMessages();
780 $this->quickCleanup([
781 'civicrm_action_schedule',
782 'civicrm_action_log',
783 'civicrm_membership',
785 'civicrm_participant',
793 * Get mailer examples.
797 public function mailerExamples(): array {
800 // Some tokens - short as subject has 128char limit in DB.
801 $someTokensTmpl = implode(';;', [
802 // basic contact token
803 '{contact.display_name}',
804 // funny legacy contact token
806 // funny legacy contact token
807 '{contact.gender_id}',
810 // action-scheduler token
811 '{activity.activity_type}',
813 // Further tokens can be tested in the body text/html.
814 $manyTokensTmpl = implode(';;', [
816 '{contact.email_greeting}',
817 $this->fixtures
['contact_custom_token']['token'],
819 // Note: The behavior of domain-tokens on a scheduled reminder is undefined. All we
820 // can really do is check that it has something.
821 $someTokensExpected = 'Churmondleia Ōtākou;;Female;;Female;;[a-zA-Z0-9 ]+;;Phone Call';
822 $manyTokensExpected = sprintf('%s;;Dear Churmondleia;;%s', $someTokensExpected, $this->fixtures
['contact_custom_token']['value']);
824 // In this example, we use a lot of tokens cutting across multiple components.
826 // Schedule definition.
828 'subject' => "subj $someTokensTmpl",
829 'body_html' => "html $manyTokensTmpl",
830 'body_text' => "text $manyTokensTmpl",
832 // Assertions (regex).
834 'from_name' => '/^FIXME$/',
835 'from_email' => '/^info@EXAMPLE.ORG$/',
836 'subject' => "/^subj $someTokensExpected\$/",
837 'body_html' => "/^html $manyTokensExpected\$/",
838 'body_text' => "/^text $manyTokensExpected\$/",
842 // In this example, we customize the from address.
844 // Schedule definition.
846 'from_name' => 'Bob',
847 'from_email' => 'bob@example.org',
849 // Assertions (regex).
851 'from_name' => '/^Bob$/',
852 'from_email' => '/^bob@example.org$/',
856 // In this example, we auto-convert HTML to text
858 // Schedule definition.
860 'body_html' => '<p>Hello & stuff.</p>',
863 // Assertions (regex).
865 'body_html' => '/^' . preg_quote('<p>Hello & stuff.</p>', '/') . '/',
866 'body_text' => '/^' . preg_quote('Hello & stuff.', '/') . '/',
870 // In this example, we autoconvert HTML to text
872 // Schedule definition.
875 'body_text' => 'Hello world',
877 // Assertions (regex).
879 'body_html' => '/^--UNDEFINED--$/',
880 'body_text' => '/^Hello world$/',
888 * This generates a single mailing through the scheduled-reminder
889 * system (using an activity-reminder as a baseline) and
890 * checks that the resulting message satisfies various
891 * regular expressions.
893 * @param array $schedule
894 * Values to set/override in the schedule.
895 * Ex: array('subject' => 'Hello, {contact.first_name}!').
896 * @param array $patterns
897 * A list of regexes to compare with the actual email.
898 * Ex: array('subject' => '/^Hello, Alice!/').
899 * Keys: subject, body_text, body_html, from_name, from_email.
901 * @throws \API_Exception
902 * @throws \CRM_Core_Exception
903 * @throws \Civi\API\Exception\UnauthorizedException
904 * @dataProvider mailerExamples
906 public function testMailer(array $schedule, array $patterns): void
{
907 $this->createScheduleFromFixtures('sched_activity_1day', $schedule);
908 $activity = $this->createTestObject('CRM_Activity_DAO_Activity', $this->fixtures
['phone_call']);
909 $contact = $this->callAPISuccess('contact', 'create', array_merge(
910 $this->fixtures
['contact'],
912 $this->fixtures
['contact_custom_token']['name'] => $this->fixtures
['contact_custom_token']['value'],
917 ActivityContact
::create(FALSE)->setValues([
918 'contact_id' => $contact['id'],
919 'activity_id' => $activity->id
,
920 'record_type_id:name' => 'Activity Source',
923 CRM_Utils_Time
::setTime('2012-06-14 15:00:00');
924 $this->callAPISuccess('job', 'send_reminder');
925 $this->mut
->assertRecipients([['test-member@example.com']]);
926 foreach ($this->mut
->getAllMessages('ezc') as $message) {
927 /** @var ezcMail $message */
930 $messageArray['subject'] = $message->subject
;
931 $messageArray['from_name'] = $message->from
->name
;
932 $messageArray['from_email'] = $message->from
->email
;
933 $messageArray['body_text'] = '--UNDEFINED--';
934 $messageArray['body_html'] = '--UNDEFINED--';
936 foreach ($message->fetchParts() as $part) {
937 /** @var ezcMailText ezcMailText */
938 if ($part instanceof ezcMailText
&& $part->subType
=== 'html') {
939 $messageArray['body_html'] = $part->text
;
941 if ($part instanceof ezcMailText
&& $part->subType
=== 'plain') {
942 $messageArray['body_text'] = $part->text
;
946 foreach ($patterns as $field => $pattern) {
947 $this->assertRegExp($pattern, $messageArray[$field],
948 "Check that '$field'' matches regex. " . print_r(['expected' => $patterns, 'actual' => $messageArray], 1));
951 $this->mut
->clearMessages();
955 * Test calculated activity schedule.
957 * @throws \API_Exception
958 * @throws \CRM_Core_Exception
960 public function testActivityDateTimeMatchNonRepeatableSchedule(): void
{
961 $this->createScheduleFromFixtures('sched_activity_1day');
963 $activity = $this->createTestObject('CRM_Activity_DAO_Activity', $this->fixtures
['phone_call']);
964 $contact = $this->callAPISuccess('contact', 'create', $this->fixtures
['contact']);
965 $activity->subject
= 'Test subject for phone_call';
968 $source['contact_id'] = $contact['id'];
969 $source['activity_id'] = $activity->id
;
970 $source['record_type_id'] = 2;
971 $activityContact = $this->createTestObject('CRM_Activity_DAO_ActivityContact', $source);
972 $activityContact->save();
974 $this->assertCronRuns([
976 // Before the 24-hour mark, no email
977 'time' => '2012-06-14 04:00:00',
982 // After the 24-hour mark, an email
983 'time' => '2012-06-14 15:00:00',
984 'recipients' => [['test-member@example.com']],
985 'subjects' => ['1-Day (non-repeating) (about Phone Call)'],
988 // Run cron again; message already sent
993 $activities = Activity
::get(FALSE)
994 ->setSelect(['details'])
995 ->addWhere('activity_type_id:name', '=', 'Reminder Sent')
996 ->addWhere('source_record_id', '=', $activity->id
)
998 foreach ($activities as $activityDetails) {
999 $this->assertContains($activity->subject
, $activityDetails['details']);
1004 * Test schedule creation on repeatable schedule.
1006 * @throws \CRM_Core_Exception
1008 public function testActivityDateTimeMatchRepeatableSchedule(): void
{
1009 $this->createScheduleFromFixtures('sched_activity_1day_r');
1010 $this->createActivityAndContactFromFixtures();
1012 $this->assertCronRuns([
1014 // Before the 24-hour mark, no email
1015 'time' => '2012-06-14 04:00:00',
1020 // After the 24-hour mark, an email
1021 'time' => '2012-06-14 15:00:00',
1022 'recipients' => [['test-member@example.com']],
1023 'subjects' => ['1-Day (repeating) (about Phone Call)'],
1026 // Run cron 4 hours later; first message already sent
1027 'time' => '2012-06-14 20:00:00',
1032 // Run cron 6 hours later; send second message.
1033 'time' => '2012-06-14 21:00:01',
1034 'recipients' => [['test-member@example.com']],
1035 'subjects' => ['1-Day (repeating) (about Phone Call)'],
1041 * @throws \CRM_Core_Exception
1043 public function testActivityDateTimeMatchRepeatableScheduleOnAbsDate(): void
{
1044 $this->createScheduleFromFixtures('sched_activity_1day_r_on_abs_date');
1045 $this->createActivityAndContactFromFixtures();
1047 $this->assertCronRuns([
1049 // Before the 24-hour mark, no email
1050 'time' => '2012-06-13 04:00:00',
1055 // On absolute date set on 2012-06-14
1056 'time' => '2012-06-14 00:00:00',
1057 'recipients' => [['test-member@example.com']],
1058 'subjects' => ['1-Day (repeating) (about Phone Call)'],
1061 // Run cron 4 hours later; first message already sent
1062 'time' => '2012-06-14 04:00:00',
1067 // Run cron 6 hours later; send second message.
1068 'time' => '2012-06-14 06:00:01',
1069 'recipients' => [['test-member@example.com']],
1070 'subjects' => ['1-Day (repeating) (about Phone Call)'],
1076 * Test event with only an absolute date.
1078 * @throws \CRM_Core_Exception
1080 public function testEventNameWithAbsoluteDateAndNothingElse(): void
{
1081 $participant = $this->createTestObject('CRM_Event_DAO_Participant', array_merge($this->fixtures
['participant'], ['status_id' => 1]));
1082 $this->callAPISuccess('Email', 'create', [
1083 'contact_id' => $participant->contact_id
,
1084 'email' => 'test-event@example.com',
1086 $this->callAPISuccess('contact', 'create', array_merge($this->fixtures
['contact'], ['contact_id' => $participant->contact_id
]));
1088 $actionSchedule = $this->fixtures
['sched_event_name_1day_on_abs_date'];
1089 $actionSchedule['entity_value'] = $participant->event_id
;
1090 $this->callAPISuccess('action_schedule', 'create', $actionSchedule);
1092 $this->assertCronRuns([
1094 // Before the 24-hour mark, no email
1095 'time' => '2012-06-13 04:00:00',
1100 // On absolute date set on 2012-06-14
1101 'time' => '2012-06-14 00:00:00',
1102 'recipients' => [['test-event@example.com']],
1103 'subjects' => ['sched_event_name_1day_on_abs_date'],
1106 // Run cron 4 hours later; first message already sent
1107 'time' => '2012-06-14 04:00:00',
1115 * For contacts/members which match schedule based on join/start date,
1116 * an email should be sent.
1118 * @throws \CRM_Core_Exception
1120 public function testMembershipDateMatch(): void
{
1121 $membership = $this->createTestObject('CRM_Member_DAO_Membership', array_merge($this->fixtures
['rolling_membership'], ['status_id' => 1]));
1122 $this->callAPISuccess('Email', 'create', [
1123 'contact_id' => $membership->contact_id
,
1124 'email' => 'test-member@example.com',
1125 'location_type_id' => 1,
1129 $this->callAPISuccess('contact', 'create', array_merge($this->fixtures
['contact'], ['contact_id' => $membership->contact_id
]));
1130 $this->createScheduleFromFixtures('sched_membership_join_2week', ['entity_value' => $membership->membership_type_id
]);
1132 // start_date=2012-03-15 ; schedule is 2 weeks after join_date
1133 $this->assertCronRuns([
1135 // Before the 2-week mark, no email.
1136 'time' => '2012-03-28 01:00:00',
1141 // After the 2-week mark, send an email.
1142 'time' => '2012-03-29 01:00:00',
1143 'recipients' => [['test-member@example.com']],
1144 'subjects' => ['subject sched_membership_join_2week (joined March 15th, 2012)'],
1148 $this->createScheduleFromFixtures('sched_membership_start_1week', ['entity_value' => $membership->membership_type_id
]);
1150 // start_date=2012-03-15 ; schedule is 1 weeks after start_date
1151 $this->assertCronRuns([
1153 // Before the 2-week mark, no email.
1154 'time' => '2012-03-21 01:00:00',
1159 // After the 2-week mark, send an email.
1160 'time' => '2012-03-22 01:00:00',
1161 'recipients' => [['test-member@example.com']],
1162 'subjects' => ['subject sched_membership_start_1week (joined March 15th, 2012)'],
1168 * CRM-21675: Support parent and smart group in 'Limit to' field
1170 * @throws \CRM_Core_Exception
1171 * @throws \CiviCRM_API3_Exception
1173 public function testScheduleReminderWithParentGroup(): void
{
1174 // Contact A with birth-date at '07-07-2005' and gender - Male, later got added in smart group
1175 $this->individualCreate(['birth_date' => '20050707', 'gender_id' => 1, 'email' => 'abc@test.com']);
1176 // Contact B with birth-date at '07-07-2005', later got added in regular group
1177 $contactID2 = $this->individualCreate(['birth_date' => '20050707', 'email' => 'def@test.com'], 1);
1178 // Contact C with birth-date at '07-07-2005', but not included in any group
1179 $this->individualCreate(['birth_date' => '20050707', 'email' => 'ghi@test.com'], 2);
1181 // create regular group and add Contact B to it
1182 $groupID = $this->groupCreate();
1183 $this->callAPISuccess('GroupContact', 'Create', [
1184 'group_id' => $groupID,
1185 'contact_id' => $contactID2,
1188 // create smart group which will contain all Male contacts
1189 $smartGroupParams = ['form_values' => ['gender_id' => 1]];
1190 $smartGroupID = $this->smartGroupCreate(
1193 'name' => 'new_smart_group',
1194 'title' => 'New Smart Group',
1195 'parents' => [$groupID => 1],
1199 $actionScheduleParams = [
1200 'name' => 'sched_contact_birth_day_yesterday',
1201 'title' => 'sched_contact_birth_day_yesterday',
1202 'absolute_date' => '',
1203 'body_html' => '<p>you look like you were born yesterday!</p>',
1204 'body_text' => 'you look like you were born yesterday!',
1207 'end_frequency_interval' => '',
1208 'end_frequency_unit' => '',
1209 'entity_status' => 1,
1210 'entity_value' => 'birth_date',
1212 'group_id' => $groupID,
1216 'msg_template_id' => '',
1218 'recipient_listing' => '',
1219 'recipient_manual' => '',
1220 'record_activity' => 1,
1221 'repetition_frequency_interval' => '',
1222 'repetition_frequency_unit' => '',
1223 'start_action_condition' => 'after',
1224 'start_action_date' => 'date_field',
1225 'start_action_offset' => '1',
1226 'start_action_unit' => 'day',
1227 'subject' => 'subject sched_contact_birth_day_yesterday',
1230 // Create schedule reminder where parent group ($groupID) is selected to limit recipients,
1231 // which contain a individual contact - $contactID2 and is parent to smart group.
1232 $this->callAPISuccess('ActionSchedule', 'create', $actionScheduleParams);
1233 $this->assertCronRuns([
1235 // On the birthday, no email.
1236 'time' => '2005-07-07 01:00:00',
1240 // The next day, send an email.
1241 'time' => '2005-07-08 20:00:00',
1252 $this->groupDelete($smartGroupID);
1253 $this->groupDelete($groupID);
1257 * Test end date email sent.
1259 * For contacts/members which match schedule based on join date,
1260 * an email should be sent.
1262 * @throws \API_Exception
1263 * @throws \CRM_Core_Exception
1265 public function testMembershipJoinDateNonMatch(): void
{
1266 $this->createMembershipFromFixture('rolling_membership', '', ['email' => 'test-member@example.com']);
1267 // Add an alternative membership type, and only send messages for that type
1268 $extraMembershipType = $this->createTestObject('CRM_Member_DAO_MembershipType', []);
1269 $this->createScheduleFromFixtures('sched_membership_join_2week', ['entity_value' => $extraMembershipType->id
]);
1271 // start_date=2012-03-15 ; schedule is 2 weeks after start_date
1272 $this->assertCronRuns([
1274 // After the 2-week mark, don't send email because we have different membership type.
1275 'time' => '2012-03-29 01:00:00',
1282 * Test that the first and SECOND notifications are sent out.
1284 * @throws \API_Exception
1285 * @throws \CRM_Core_Exception
1287 public function testMembershipEndDateRepeat(): void
{
1288 // creates membership with end_date = 20120615
1289 $membership = $this->createMembershipFromFixture('rolling_membership', 'Current');
1290 $this->callAPISuccess('Email', 'create', [
1291 'contact_id' => $membership->contact_id
,
1292 'email' => 'test-member@example.com',
1294 $this->callAPISuccess('contact', 'create', array_merge($this->fixtures
['contact'], ['contact_id' => $membership->contact_id
]));
1296 $this->createScheduleFromFixtures('sched_membership_end_2month_repeat_twice_4_weeks', ['entity_value' => $membership->membership_type_id
]);
1298 // end_date=2012-06-15 ; schedule is 2 weeks before end_date
1299 $this->assertCronRuns([
1301 // After the 1-month mark, no email
1302 'time' => '2012-07-15 01:00:00',
1306 // After the 2-month mark, send an email.
1307 'time' => '2012-08-15 01:00:00',
1308 'recipients' => [['test-member@example.com']],
1311 // 4 weeks after first email send first repeat
1312 'time' => '2012-09-12 01:00:00',
1313 'recipients' => [['test-member@example.com']],
1316 // 1 week after first repeat send nothing
1317 // There was a bug where the first repeat went out and then
1318 // it would keep going out every cron run. This is to check that's
1320 'time' => '2012-09-19 01:00:00',
1324 // 4 weeks after first repeat send second repeat
1325 'time' => '2012-10-10 01:00:00',
1326 'recipients' => [['test-member@example.com']],
1329 // 4 months after membership end, send nothing
1330 'time' => '2012-10-15 01:00:00',
1334 // 5 months after membership end, send nothing
1335 'time' => '2012-11-15 01:00:00',
1342 * Test behaviour when date changes.
1344 * Test that the first notification is sent but the second is NOT sent if the end date changes in
1348 * @throws \API_Exception
1349 * @throws \CRM_Core_Exception
1351 public function testMembershipEndDateRepeatChangedEndDate_CRM_15376(): void
{
1352 // creates membership with end_date = 20120615
1353 $membership = $this->createMembershipFromFixture('rolling_membership', 'Current');
1354 $this->callAPISuccess('Email', 'create', [
1355 'contact_id' => $membership->contact_id
,
1356 'email' => 'test-member@example.com',
1358 $this->callAPISuccess('contact', 'create', array_merge($this->fixtures
['contact'], ['contact_id' => $membership->contact_id
]));
1360 $this->createScheduleFromFixtures('sched_membership_end_2month_repeat_twice_4_weeks', ['entity_value' => $membership->membership_type_id
]);
1361 // end_date=2012-06-15 ; schedule is 2 weeks before end_date
1362 $this->assertCronRuns([
1364 // After the 2-week mark, send an email.
1365 'time' => '2012-08-15 01:00:00',
1366 'recipients' => [['test-member@example.com']],
1370 // Extend membership - reminder should NOT go out.
1371 $this->callAPISuccess('membership', 'create', ['id' => $membership->id
, 'end_date' => '2014-01-01']);
1372 $this->assertCronRuns([
1374 // After the 2-week mark, send an email.
1375 'time' => '2012-09-12 01:00:00',
1382 * Test membership end date email sends.
1384 * For contacts/members which match schedule based on end date,
1385 * an email should be sent.
1387 * @throws \API_Exception
1388 * @throws \CRM_Core_Exception
1390 public function testMembershipEndDateMatch(): void
{
1391 // creates membership with end_date = 20120615
1392 $membership = $this->createMembershipFromFixture('rolling_membership', 'Current');
1393 $this->callAPISuccess('Email', 'create', [
1394 'contact_id' => $membership->contact_id
,
1395 'email' => 'test-member@example.com',
1397 $this->callAPISuccess('contact', 'create', array_merge($this->fixtures
['contact'], ['contact_id' => $membership->contact_id
]));
1399 $this->createScheduleFromFixtures('sched_membership_end_2week', ['entity_value' => $membership->membership_type_id
]);
1401 // end_date=2012-06-15 ; schedule is 2 weeks before end_date
1402 $this->assertCronRuns([
1404 // Before the 2-week mark, no email.
1405 'time' => '2012-05-31 01:00:00',
1409 // After the 2-week mark, send an email.
1410 'time' => '2012-06-01 01:00:00',
1411 'recipients' => [['test-member@example.com']],
1414 // After the email is sent, another one is not sent
1415 'time' => '2012-06-01 02:00:00',
1420 // Now suppose user has renewed for rolling membership after 3 months, so upcoming assertion is written
1421 // to ensure that new reminder is sent 2 week before the new end_date i.e. '2012-09-15'
1422 $membership->end_date
= '2012-09-15';
1423 $membership->save();
1425 //change the email id of chosen membership contact to assert
1426 //recipient of not the previously sent mail but the new one
1427 $result = $this->callAPISuccess('Email', 'create', [
1429 'contact_id' => $membership->contact_id
,
1430 'email' => 'member2@example.com',
1432 $this->assertAPISuccess($result);
1434 // end_date=2012-09-15 ; schedule is 2 weeks before end_date
1435 $this->assertCronRuns([
1437 // Before the 2-week mark, no email
1438 'time' => '2012-08-31 01:00:00',
1442 // After the 2-week mark, send an email
1443 'time' => '2012-09-01 01:00:00',
1444 'recipients' => [['member2@example.com']],
1447 // After the email is sent, another one is not sent
1448 'time' => '2012-09-01 02:00:00',
1453 $membership->end_date
= '2012-12-15';
1454 $membership->save();
1455 // end_date=2012-12-15 ; schedule is 2 weeks before end_date
1456 $this->assertCronRuns([
1458 // Before the 2-week mark, no email
1459 'time' => '2012-11-30 01:00:00',
1463 // After the 2-week mark, send an email
1464 'time' => '2012-12-01 01:00:00',
1465 'recipients' => [['member2@example.com']],
1468 // After the email is sent, another one is not sent
1469 'time' => '2012-12-01 02:00:00',
1477 * @param array $contactFixture
1478 * @param int $membershipTypeId
1480 * @return array|NULL|object
1481 * @throws \CRM_Core_Exception
1483 public function createMembershipAndContact(array $contactFixture, int $membershipTypeId) {
1484 $result = $this->callAPISuccess('contact', 'create', $contactFixture);
1485 $contact = $result['values'][$result['id']];
1488 'contact_id' => $contact['id'],
1489 'membership_type_id' => $membershipTypeId,
1490 'owner_membership_id' => 'NULL',
1492 $params = array_merge($this->fixtures
['rolling_membership'], $params);
1493 return $this->createTestObject('CRM_Member_DAO_Membership', $params);
1497 * This test is very similar to testMembershipEndDateMatch, but it adds
1498 * another contact because there was a bug in
1499 * RecipientBuilder::buildRelFirstPass where it was only sending the
1500 * reminder for the first contact returned in a query for renewed
1501 * memberships. Other contacts wouldn't get the mail.
1503 * @throws \CRM_Core_Exception
1505 public function testMultipleMembershipEndDateMatch(): void
{
1506 $membershipTypeId = $this->membershipTypeCreate($this->fixtures
['rolling_membership']['membership_type_id']);
1507 $membershipOne = $this->createMembershipAndContact($this->fixtures
['contact'], $membershipTypeId);
1508 $membershipTwo = $this->createMembershipAndContact($this->fixtures
['contact_2'], $membershipTypeId);
1509 $this->createScheduleFromFixtures('sched_membership_end_2week', ['entity_value' => $membershipTypeId]);
1511 // end_date=2012-06-15 ; schedule is 2 weeks before end_date
1512 $this->assertCronRuns([
1514 // Before the 2-week mark, no email.
1515 'time' => '2012-05-31 01:00:00',
1519 // After the 2-week mark, send emails.
1520 'time' => '2012-06-01 01:00:00',
1522 ['test-member@example.com'],
1523 ['test-contact-2@example.com'],
1527 // After the email is sent, another one is not sent
1528 'time' => '2012-06-01 02:00:00',
1533 // Now suppose user has renewed for rolling membership after 3 months, so upcoming assertion is written
1534 // to ensure that new reminder is sent 2 week before the new end_date i.e. '2012-09-15'
1535 $membershipOne->end_date
= '2012-09-15';
1536 $membershipOne->save();
1537 $membershipTwo->end_date
= '2012-09-15';
1538 $membershipTwo->save();
1540 // end_date=2012-09-15 ; schedule is 2 weeks before end_date
1541 $this->assertCronRuns([
1543 // Before the 2-week mark, no email
1544 'time' => '2012-08-31 01:00:00',
1548 // After the 2-week mark, send an email
1549 'time' => '2012-09-01 01:00:00',
1551 ['test-member@example.com'],
1552 ['test-contact-2@example.com'],
1556 // After the email is sent, another one is not sent
1557 'time' => '2012-06-01 02:00:00',
1564 * Test membership end date email.
1566 * For contacts/members which match schedule based on end date,
1567 * an email should be sent.
1569 * @throws \API_Exception
1570 * @throws \CRM_Core_Exception
1572 public function testMembershipEndDateNoMatch(): void
{
1573 // creates membership with end_date = 20120615
1574 $membership = $this->createMembershipFromFixture('rolling_membership', 'Grace');
1575 $this->callAPISuccess('Email', 'create', [
1576 'contact_id' => $membership->contact_id
,
1577 'email' => 'test-member@example.com',
1579 $this->callAPISuccess('contact', 'create', array_merge($this->fixtures
['contact'], ['contact_id' => $membership->contact_id
]));
1580 $this->createScheduleFromFixtures('sched_membership_end_2month', ['entity_value' => $membership->membership_type_id
]);
1582 // end_date=2012-06-15 ; schedule is 2 weeks before end_date
1583 $this->assertCronRuns([
1585 // Before the 2-week mark, no email.
1586 'time' => '2012-05-31 01:00:00',
1590 // After the 2-week mark, no email
1591 'time' => '2013-05-01 01:00:00',
1598 * @throws \CRM_Core_Exception
1600 public function testContactBirthDateNoAnniversary(): void
{
1601 $contact = $this->callAPISuccess('Contact', 'create', $this->fixtures
['contact_birthdate']);
1602 $this->_testObjects
['CRM_Contact_DAO_Contact'][] = $contact['id'];
1603 $this->createScheduleFromFixtures('sched_contact_birth_day_yesterday');
1604 $this->assertCronRuns([
1606 // On the birthday, no email.
1607 'time' => '2005-07-07 01:00:00',
1611 // The next day, send an email.
1612 'time' => '2005-07-08 20:00:00',
1613 'recipients' => [['test-birth_day@example.com']],
1619 * @throws \CRM_Core_Exception
1621 public function testContactBirthDateAnniversary(): void
{
1622 $contact = $this->callAPISuccess('Contact', 'create', $this->fixtures
['contact_birthdate']);
1623 $this->_testObjects
['CRM_Contact_DAO_Contact'][] = $contact['id'];
1624 $this->createScheduleFromFixtures('sched_contact_birth_day_anniversary');
1625 $this->assertCronRuns([
1627 // On some random day, no email.
1628 'time' => '2014-03-07 01:00:00',
1632 // On the eve of their 9th birthday, send an email.
1633 'time' => '2014-07-06 20:00:00',
1634 'recipients' => [['test-birth_day@example.com']],
1640 * @throws \CRM_Core_Exception
1642 public function testContactCustomDateNoAnniversary(): void
{
1644 'title' => 'Test_Group',
1645 'name' => 'test_group',
1646 'extends' => ['Individual'],
1647 'style' => 'Inline',
1648 'is_multiple' => FALSE,
1651 $createGroup = $this->callAPISuccess('custom_group', 'create', $group);
1653 'label' => 'Graduation',
1654 'data_type' => 'Date',
1655 'html_type' => 'Select Date',
1656 'custom_group_id' => $createGroup['id'],
1658 $createField = $this->callAPISuccess('custom_field', 'create', $field);
1659 $contactParams = $this->fixtures
['contact'];
1660 $contactParams["custom_{$createField['id']}"] = '2013-12-16';
1661 $contact = $this->callAPISuccess('Contact', 'create', $contactParams);
1662 $this->_testObjects
['CRM_Contact_DAO_Contact'][] = $contact['id'];
1663 $this->createScheduleFromFixtures('sched_contact_grad_tomorrow', ['entity_value' => "custom_{$createField['id']}"]);
1664 $this->assertCronRuns([
1666 // On some random day, no email.
1667 'time' => '2014-03-07 01:00:00',
1671 // On the eve of their graduation, send an email.
1672 'time' => '2013-12-15 20:00:00',
1673 'recipients' => [['test-member@example.com']],
1676 $this->callAPISuccess('custom_group', 'delete', ['id' => $createGroup['id']]);
1680 * @throws \CRM_Core_Exception
1682 public function testContactCreatedNoAnniversary(): void
{
1683 $contact = $this->callAPISuccess('Contact', 'create', $this->fixtures
['contact_birthdate']);
1684 $this->_testObjects
['CRM_Contact_DAO_Contact'][] = $contact['id'];
1685 $this->createScheduleFromFixtures('sched_contact_created_yesterday');
1686 $this->assertCronRuns([
1688 // On the date created, no email.
1689 'time' => $contact['values'][$contact['id']]['created_date'],
1693 // The next day, send an email.
1694 'time' => date('Y-m-d H:i:s', strtotime($contact['values'][$contact['id']]['created_date'] . ' +1 day')),
1695 'recipients' => [['test-birth_day@example.com']],
1701 * Test the impact of changing the anniversary.
1703 * @throws \CRM_Core_Exception
1705 public function testContactModifiedAnniversary(): void
{
1706 $contact = $this->callAPISuccess('Contact', 'create', $this->fixtures
['contact_birthdate']);
1707 $this->_testObjects
['CRM_Contact_DAO_Contact'][] = $contact['id'];
1708 $modifiedDate = $this->callAPISuccess('Contact', 'getvalue', ['id' => $contact['id'], 'return' => 'modified_date']);
1709 $this->createScheduleFromFixtures('sched_contact_mod_anniversary');
1710 $this->assertCronRuns([
1712 // On some random day, no email.
1713 'time' => date('Y-m-d H:i:s', strtotime($contact['values'][$contact['id']]['modified_date'] . ' -60 days')),
1717 // On the eve of 3 years after they were modified, send an email.
1718 'time' => date('Y-m-d H:i:s', strtotime($modifiedDate . ' +3 years -1 day')),
1719 'recipients' => [['test-birth_day@example.com']],
1725 * Check that limit_to + an empty recipients doesn't sent to multiple contacts.
1727 * @throws \API_Exception
1728 * @throws \CRM_Core_Exception
1730 public function testMembershipLimitToNone(): void
{
1731 // creates membership with end_date = 20120615
1732 $membership = $this->createMembershipFromFixture('rolling_membership', 'Current');
1733 $result = $this->callAPISuccess('Email', 'create', [
1734 'contact_id' => $membership->contact_id
,
1735 'email' => 'member@example.com',
1737 $this->callAPISuccess('contact', 'create', array_merge($this->fixtures
['contact'], ['contact_id' => $membership->contact_id
]));
1738 $this->callAPISuccess('contact', 'create', ['email' => 'b@c.com', 'contact_type' => 'Individual']);
1740 $this->assertAPISuccess($result);
1742 $this->createScheduleFromFixtures('sched_membership_end_limit_to_none', ['entity_value' => $membership->membership_type_id
]);
1744 // end_date=2012-06-15 ; schedule is 2 weeks before end_date
1745 $this->assertCronRuns([
1747 // Before the 2-week mark, no email.
1748 'time' => '2012-05-31 01:00:00',
1755 * Test handling of reference date for memberships.
1757 * @throws \API_Exception
1758 * @throws \CRM_Core_Exception
1760 public function testMembershipWithReferenceDate(): void
{
1761 $membership = $this->createMembershipFromFixture('rolling_membership', 'Current', ['email' => 'member@example.com']);
1762 $this->callAPISuccess('contact', 'create', array_merge($this->fixtures
['contact'], ['contact_id' => $membership->contact_id
]));
1764 $this->createScheduleFromFixtures('sched_membership_join_2week', ['entity_value' => $membership->membership_type_id
]);
1766 // start_date=2012-03-15 ; schedule is 2 weeks after start_date
1767 $this->assertCronRuns([
1769 // After the 2-week mark, send an email
1770 'time' => '2012-03-29 01:00:00',
1771 'recipients' => [['member@example.com']],
1774 // After the 2-week 1day mark, don't send an email
1775 'time' => '2012-03-30 01:00:00',
1780 //check if reference date is set to membership's join date
1781 //as per the action_start_date chosen for current schedule reminder
1782 $this->assertEquals('2012-03-15 00:00:00',
1783 CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_ActionLog', $membership->contact_id
, 'reference_date', 'contact_id')
1786 //change current membership join date that may signifies as membership renewal activity
1787 $membership->join_date
= '2012-03-29';
1788 $membership->save();
1790 $this->assertCronRuns([
1792 // After the 13 days of the changed join date 2012-03-29, don't send an email
1793 'time' => '2012-04-11 01:00:00',
1797 // After the 2-week of the changed join date 2012-03-29, send an email
1798 'time' => '2012-04-12 01:00:00',
1799 'recipients' => [['member@example.com']],
1802 $this->assertCronRuns([
1804 // It should not re-send on the same day
1805 'time' => '2012-04-12 01:00:00',
1812 * Test multiple membership reminder.
1814 * @throws \API_Exception
1815 * @throws \CRM_Core_Exception
1817 public function testMembershipOnMultipleReminder(): void
{
1818 $membership = $this->createMembershipFromFixture('rolling_membership', 'Current', ['email' => 'member@example.com']);
1819 $this->callAPISuccess('contact', 'create', array_merge($this->fixtures
['contact'], ['contact_id' => $membership->contact_id
]));
1821 // Send email 2 weeks before end_date
1822 $actionScheduleBefore = $this->fixtures
['sched_membership_end_2week'];
1823 // Send email on end_date/expiry date
1824 $actionScheduleOn = $this->fixtures
['sched_on_membership_end_date'];
1825 // Send email 1 day after end_date/grace period
1826 $actionScheduleAfter = $this->fixtures
['sched_after_1day_membership_end_date'];
1827 $actionScheduleBefore['entity_value'] = $actionScheduleOn['entity_value'] = $actionScheduleAfter['entity_value'] = $membership->membership_type_id
;
1828 foreach (['actionScheduleBefore', 'actionScheduleOn', 'actionScheduleAfter'] as $value) {
1829 $
$value = CRM_Core_BAO_ActionSchedule
::add($
$value);
1832 $this->assertCronRuns(
1835 // 1day 2weeks before membership end date(MED), don't send mail
1836 'time' => '2012-05-31 01:00:00',
1840 // 2 weeks before MED, send an email
1841 'time' => '2012-06-01 01:00:00',
1842 'recipients' => [['member@example.com']],
1845 // 1day before MED, don't send mail
1846 'time' => '2012-06-14 01:00:00',
1850 // On MED, send an email
1851 'time' => '2012-06-15 00:00:00',
1852 'recipients' => [['member@example.com']],
1855 // After 1day of MED, send an email
1856 'time' => '2012-06-16 01:00:00',
1857 'recipients' => [['member@example.com']],
1860 // After 1day 1min of MED, don't send an email
1861 'time' => '2012-06-17 00:01:00',
1867 // Assert the timestamp as of when the emails of respective three reminders as configured
1868 // 2 weeks before, on and 1 day after MED, are sent
1869 $this->assertApproxEquals(
1870 strtotime('2012-06-01 01:00:00'),
1871 strtotime(CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_ActionLog', $actionScheduleBefore->id
, 'action_date_time', 'action_schedule_id', TRUE)),
1872 // Variation in test execution time.
1875 $this->assertApproxEquals(
1876 strtotime('2012-06-15 00:00:00'),
1877 strtotime(CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_ActionLog', $actionScheduleOn->id
, 'action_date_time', 'action_schedule_id', TRUE)),
1878 // Variation in test execution time.
1881 $this->assertApproxEquals(
1882 strtotime('2012-06-16 01:00:00'),
1883 strtotime(CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_ActionLog', $actionScheduleAfter->id
, 'action_date_time', 'action_schedule_id', TRUE)),
1884 // Variation in test execution time.
1888 //extend MED to 2 weeks after the current MED (that may signifies as membership renewal activity)
1889 // and lets assert as of when the new set of reminders will be sent against their respective Schedule Reminders(SR)
1890 $membership->end_date
= '2012-06-20';
1891 $membership->save();
1893 $this->callAPISuccess('Contact', 'get', ['id' => $membership->contact_id
]);
1894 $this->assertCronRuns(
1897 // 1day 2weeks before membership end date(MED), don't send mail
1898 'time' => '2012-06-05 01:00:00',
1902 // 2 weeks before MED, send an email
1903 'time' => '2012-06-06 01:00:00',
1904 'recipients' => [['member@example.com']],
1907 // 1day before MED, don't send mail
1908 'time' => '2012-06-19 01:00:00',
1912 // On MED, send an email
1913 'time' => '2012-06-20 00:00:00',
1914 'recipients' => [['member@example.com']],
1917 // After 1day of MED, send an email
1918 'time' => '2012-06-21 01:00:00',
1919 'recipients' => [['member@example.com']],
1922 // After 1day 1min of MED, don't send an email
1923 'time' => '2012-07-21 00:01:00',
1930 * Test reminders sent on custom data anniversary.
1932 * @throws \API_Exception
1933 * @throws \CRM_Core_Exception
1935 public function testContactCustomDate_Anniversary(): void
{
1936 $this->createCustomGroupWithFieldOfType([], 'date');
1937 $contactParams = $this->fixtures
['contact'];
1938 $contactParams[$this->getCustomFieldName('date')] = '2013-12-16';
1939 $contact = $this->callAPISuccess('Contact', 'create', $contactParams);
1940 $this->_testObjects
['CRM_Contact_DAO_Contact'][] = $contact['id'];
1941 $this->fixtures
['sched_contact_grad_anniversary']['entity_value'] = $this->getCustomFieldName('date');
1942 $this->createScheduleFromFixtures('sched_contact_grad_anniversary');
1944 $this->assertCronRuns([
1946 // On some random day, no email.
1947 'time' => '2014-03-07 01:00:00',
1951 // A week after their 5th anniversary of graduation, send an email.
1952 'time' => '2018-12-23 20:00:00',
1953 'recipients' => [['test-member@example.com']],
1959 * Test sched reminder set via registration date.
1961 * @throws \CRM_Core_Exception
1962 * @throws \CiviCRM_API3_Exception
1964 public function testEventTypeRegistrationDate(): void
{
1965 $contact = $this->individualCreate(['email' => 'test-event@example.com']);
1966 //Add it as a participant to an event ending registration - 7 days from now.
1968 'start_date' => date('Ymd', strtotime('-5 day')),
1969 'end_date' => date('Ymd', strtotime('+7 day')),
1970 'registration_start_date' => date('Ymd', strtotime('-5 day')),
1971 'registration_end_date' => date('Ymd', strtotime('+7 day')),
1973 $event = $this->eventCreate($params);
1974 $this->participantCreate(['contact_id' => $contact, 'event_id' => $event['id']]);
1976 //Create a scheduled reminder to send email 7 days before registration date.
1977 $actionSchedule = $this->fixtures
['sched_event_type_start_1week_before'];
1978 $actionSchedule['start_action_offset'] = 7;
1979 $actionSchedule['start_action_unit'] = 'day';
1980 $actionSchedule['start_action_date'] = 'registration_end_date';
1981 $actionSchedule['entity_value'] = $event['values'][$event['id']]['event_type_id'];
1982 $actionSchedule['entity_status'] = $this->callAPISuccessGetValue('ParticipantStatusType', [
1984 'name' => 'Attended',
1986 $actionSched = $this->callAPISuccess('action_schedule', 'create', $actionSchedule);
1987 //Run the cron and verify if an email was sent.
1988 $this->assertCronRuns([
1990 'time' => date('Y-m-d'),
1991 'recipients' => [['test-event@example.com']],
1997 'email' => 'test-event2@example.com',
1999 $contact2 = $this->individualCreate($contactParams);
2000 //Create an event with registration end date = 2 week from now.
2001 $params['end_date'] = date('Ymd', strtotime('+2 week'));
2002 $params['registration_end_date'] = date('Ymd', strtotime('+2 week'));
2003 $event2 = $this->eventCreate($params);
2004 $this->participantCreate(['contact_id' => $contact2, 'event_id' => $event2['id']]);
2006 //Assert there is no reminder sent to the contact.
2007 $this->assertCronRuns([
2009 'time' => date('Y-m-d'),
2014 //Modify the sched reminder to be sent 2 week from registration end date.
2015 $this->callAPISuccess('action_schedule', 'create', [
2016 'id' => $actionSched['id'],
2017 'start_action_offset' => 2,
2018 'start_action_unit' => 'week',
2021 //Contact should receive the reminder now.
2022 $this->assertCronRuns([
2024 'time' => date('Y-m-d'),
2025 'recipients' => [['test-event2@example.com']],
2031 * Test sched reminder set via start date.
2033 * @throws \CRM_Core_Exception
2035 public function testEventTypeStartDate(): void
{
2036 // Create event+participant with start_date = 20120315, end_date = 20120615.
2037 $participant = $this->createTestObject('CRM_Event_DAO_Participant', array_merge($this->fixtures
['participant'], ['status_id' => 2]));
2038 $this->callAPISuccess('Email', 'create', [
2039 'contact_id' => $participant->contact_id
,
2040 'email' => 'test-event@example.com',
2042 $this->callAPISuccess('contact', 'create', array_merge($this->fixtures
['contact'], ['contact_id' => $participant->contact_id
]));
2044 $actionSchedule = $this->fixtures
['sched_event_type_start_1week_before'];
2045 $actionSchedule['entity_value'] = CRM_Core_DAO
::getFieldValue('CRM_Event_DAO_Event', $participant->event_id
, 'event_type_id');
2046 $this->callAPISuccess('action_schedule', 'create', $actionSchedule);
2048 //echo "CREATED\n"; ob_flush(); sleep(20);
2050 // end_date=2012-06-15 ; schedule is 2 weeks before end_date
2051 $this->assertCronRuns([
2054 'time' => '2012-03-02 01:00:00',
2059 'time' => '2012-03-08 01:00:00',
2060 'recipients' => [['test-event@example.com']],
2063 // And then nothing else
2064 'time' => '2012-03-16 01:00:00',
2071 * Test schedule on event end date.
2073 * @throws \CRM_Core_Exception
2075 public function testEventTypeEndDateRepeat(): void
{
2076 // Create event+participant with start_date = 20120315, end_date = 20120615.
2077 $participant = $this->createTestObject('CRM_Event_DAO_Participant', array_merge($this->fixtures
['participant'], ['status_id' => 2]));
2078 $this->callAPISuccess('Email', 'create', [
2079 'contact_id' => $participant->contact_id
,
2080 'email' => 'test-event@example.com',
2082 $this->callAPISuccess('contact', 'create', array_merge($this->fixtures
['contact'], ['contact_id' => $participant->contact_id
]));
2084 $actionSchedule = $this->fixtures
['sched_event_type_end_2month_repeat_twice_2_weeks'];
2085 $actionSchedule['entity_value'] = CRM_Core_DAO
::getFieldValue('CRM_Event_DAO_Event', $participant->event_id
, 'event_type_id');
2086 $this->callAPISuccess('action_schedule', 'create', $actionSchedule);
2088 $this->assertCronRuns([
2091 'time' => '2012-08-13 01:00:00',
2095 // After the 2-month mark, send an email.
2096 'time' => '2012-08-16 01:00:00',
2097 'recipients' => [['test-event@example.com']],
2100 // After 2 months and 1 week, don't repeat yet.
2101 'time' => '2012-08-23 02:00:00',
2105 // After 2 months and 2 weeks
2106 'time' => '2012-08-30 02:00:00',
2107 'recipients' => [['test-event@example.com']],
2110 // After 2 months and 4 week
2111 'time' => '2012-09-13 02:00:00',
2112 'recipients' => [['test-event@example.com']],
2115 // After 2 months and 6 weeks
2116 'time' => '2012-09-27 01:00:00',
2123 * Run a series of cron jobs and make an assertion about email deliveries.
2125 * @param array $cronRuns
2126 * array specifying when to run cron and what messages to expect; each item is an array with keys:
2127 * - time: string, e.g. '2012-06-15 21:00:01'
2128 * - recipients: array(array(string)), list of email addresses which should receive messages
2130 * @throws \CRM_Core_Exception
2131 * @noinspection DisconnectedForeachInstructionInspection
2133 public function assertCronRuns(array $cronRuns): void
{
2134 foreach ($cronRuns as $cronRun) {
2135 CRM_Utils_Time
::setTime($cronRun['time']);
2136 $this->callAPISuccess('job', 'send_reminder', []);
2137 $this->mut
->assertRecipients($cronRun['recipients']);
2138 if (array_key_exists('subjects', $cronRun)) {
2139 $this->mut
->assertSubjects($cronRun['subjects']);
2141 $this->mut
->clearMessages();
2148 * (DAO_Name => array(int)) List of items to garbage-collect during tearDown
2150 private $_testObjects;
2153 * Sets up the fixture, for example, opens a network connection.
2155 * This method is called before a test is executed.
2157 protected function _setUp(): void
{
2158 $this->_testObjects
= [];
2162 * Tears down the fixture, for example, closes a network connection.
2164 * This method is called after a test is executed.
2166 protected function _tearDown(): void
{
2168 $this->deleteTestObjects();
2172 * This is a wrapper for CRM_Core_DAO::createTestObject which tracks
2173 * created entities and provides for brainless cleanup.
2175 * @see CRM_Core_DAO::createTestObject
2178 * @param array $params
2179 * @param int $numObjects
2180 * @param bool $createOnly
2182 * @return array|NULL|object
2184 public function createTestObject($daoName, $params = [], $numObjects = 1, $createOnly = FALSE) {
2185 $objects = CRM_Core_DAO
::createTestObject($daoName, $params, $numObjects, $createOnly);
2186 if (is_array($objects)) {
2187 $this->registerTestObjects($objects);
2190 $this->registerTestObjects([$objects]);
2196 * @param array $objects
2197 * DAO or BAO objects.
2199 public function registerTestObjects(array $objects): void
{
2200 //if (is_object($objects)) {
2201 // $objects = array($objects);
2203 foreach ($objects as $object) {
2204 $daoName = str_replace('_BAO_', '_DAO_', get_class($object));
2205 $this->_testObjects
[$daoName][] = $object->id
;
2209 public function deleteTestObjects(): void
{
2210 // Note: You might argue that the FK relations between test
2211 // objects could make this problematic; however, it should
2212 // behave intuitively as long as we mentally split our
2213 // test-objects between the "manual/primary records"
2214 // and the "automatic/secondary records"
2215 foreach ($this->_testObjects
as $daoName => $daoIds) {
2216 foreach ($daoIds as $daoId) {
2217 CRM_Core_DAO
::deleteTestObjects($daoName, ['id' => $daoId]);
2220 $this->_testObjects
= [];
2224 * Test that the various repetition units work correctly.
2226 * @see https://issues.civicrm.org/jira/browse/CRM-17028
2227 * @throws \CRM_Core_Exception
2229 public function testRepetitionFrequencyUnit(): void
{
2230 $membershipTypeParams = [
2231 'duration_interval' => '1',
2232 'duration_unit' => 'year',
2234 'period_type' => 'rolling',
2236 $membershipType = $this->createTestObject('CRM_Member_DAO_MembershipType', $membershipTypeParams);
2237 $interval_units = ['hour', 'day', 'week', 'month', 'year'];
2238 foreach ($interval_units as $interval_unit) {
2239 $membershipEndDate = DateTime
::createFromFormat('Y-m-d H:i:s', '2013-03-15 00:00:00');
2241 'contact_type' => 'Individual',
2242 'first_name' => 'Test',
2243 'last_name' => "Interval $interval_unit",
2246 $contact = $this->createTestObject('CRM_Contact_DAO_Contact', $contactParams);
2248 'contact_id' => $contact->id
,
2250 'email' => "test-member-{$interval_unit}@example.com",
2251 'location_type_id' => 1,
2253 $this->createTestObject('CRM_Core_DAO_Email', $emailParams);
2254 $membershipParams = [
2255 'membership_type_id' => $membershipType->id
,
2256 'contact_id' => $contact->id
,
2257 'join_date' => '20120315',
2258 'start_date' => '20120315',
2259 'end_date' => '20130315',
2263 $membershipParams['status-id'] = 1;
2264 $membership = $this->createTestObject('CRM_Member_DAO_Membership', $membershipParams);
2265 $actionScheduleParams = $this->fixtures
['sched_on_membership_end_date_repeat_interval'];
2266 $actionScheduleParams['entity_value'] = $membershipType->id
;
2267 $actionScheduleParams['repetition_frequency_unit'] = $interval_unit;
2268 $actionScheduleParams['repetition_frequency_interval'] = 2;
2269 $actionSchedule = CRM_Core_BAO_ActionSchedule
::add($actionScheduleParams);
2270 $beforeEndDate = $this->createModifiedDateTime($membershipEndDate, '-1 day');
2271 $beforeFirstUnit = $this->createModifiedDateTime($membershipEndDate, "+1 $interval_unit");
2272 $afterFirstUnit = $this->createModifiedDateTime($membershipEndDate, "+2 $interval_unit");
2275 'time' => $beforeEndDate->format('Y-m-d H:i:s'),
2279 'time' => $membershipEndDate->format('Y-m-d H:i:s'),
2280 'recipients' => [["test-member-{$interval_unit}@example.com"]],
2283 'time' => $beforeFirstUnit->format('Y-m-d H:i:s'),
2287 'time' => $afterFirstUnit->format('Y-m-d H:i:s'),
2288 'recipients' => [["test-member-{$interval_unit}@example.com"]],
2291 $this->assertCronRuns($cronRuns);
2292 $actionSchedule->delete();
2293 $membership->delete();
2298 * Inherited members without permission to edit the main member contact should
2299 * not get reminders.
2301 * However, just because a contact inherits one membership doesn't mean
2302 * reminders for other memberships should be suppressed.
2306 * @throws \CRM_Core_Exception
2308 public function testInheritedMembershipPermissions(): void
{
2309 // Set up common parameters for memberships.
2310 $membershipParams = $this->fixtures
['rolling_membership'];
2311 $membershipParams['status_id'] = 1;
2313 $membershipParams['membership_type_id']['relationship_type_id'] = 1;
2314 $membershipParams['membership_type_id']['relationship_direction'] = 'b_a';
2315 $membershipType1 = $this->createTestObject('CRM_Member_DAO_MembershipType', $membershipParams['membership_type_id']);
2317 // We'll create a new membership type that can be held at the same time as
2319 $membershipParams['membership_type_id']['relationship_type_id'] = 'NULL';
2320 $membershipParams['membership_type_id']['relationship_direction'] = 'NULL';
2321 $membershipType2 = $this->createTestObject('CRM_Member_DAO_MembershipType', $membershipParams['membership_type_id']);
2323 // Create the parent membership and contact
2324 $membershipParams['membership_type_id'] = $membershipType1->id
;
2325 $mainMembership = $this->createTestObject('CRM_Member_DAO_Membership', $membershipParams);
2328 'contact_type' => 'Individual',
2329 'first_name' => 'Mom',
2330 'last_name' => 'Rel',
2333 $this->createTestObject('CRM_Contact_DAO_Contact', array_merge($contactParams, ['id' => $mainMembership->contact_id
]));
2336 'contact_id' => $mainMembership->contact_id
,
2337 'email' => 'test-member@example.com',
2338 'location_type_id' => 1,
2341 $this->createTestObject('CRM_Core_DAO_Email', $emailParams);
2343 // Set up contacts and emails for the two children
2344 $contactParams['first_name'] = 'Favorite';
2345 $permChild = $this->createTestObject('CRM_Contact_DAO_Contact', $contactParams);
2346 $emailParams['email'] = 'favorite@example.com';
2347 $emailParams['contact_id'] = $permChild->id
;
2348 $this->createTestObject('CRM_Core_DAO_Email', $emailParams);
2350 $contactParams['first_name'] = 'Black Sheep';
2351 $nonPermChild = $this->createTestObject('CRM_Contact_DAO_Contact', $contactParams);
2352 $emailParams['email'] = 'black.sheep@example.com';
2353 $emailParams['contact_id'] = $nonPermChild->id
;
2354 $this->createTestObject('CRM_Core_DAO_Email', $emailParams);
2356 // Each child gets a relationship, one with permission to edit the parent. This
2357 // will trigger inherited memberships for the first membership type
2359 'relationship_type_id' => 1,
2360 'contact_id_a' => $nonPermChild->id
,
2361 'contact_id_b' => $mainMembership->contact_id
,
2364 $this->callAPISuccess('relationship', 'create', $relParams);
2366 $relParams['contact_id_a'] = $permChild->id
;
2367 $relParams['is_permission_a_b'] = CRM_Contact_BAO_Relationship
::EDIT
;
2368 $this->callAPISuccess('relationship', 'create', $relParams);
2370 // Mom and Black Sheep get their own memberships of the second type.
2371 $membershipParams['membership_type_id'] = $membershipType2->id
;
2372 $membershipParams['owner_membership_id'] = 'NULL';
2373 $membershipParams['contact_id'] = $mainMembership->contact_id
;
2374 $this->createTestObject('CRM_Member_DAO_Membership', $membershipParams);
2376 $membershipParams['contact_id'] = $nonPermChild->id
;
2377 $this->createTestObject('CRM_Member_DAO_Membership', $membershipParams);
2379 // Test a reminder for the first membership type - that should exclude Black
2381 $this->fixtures
['sched_membership_join_2week']['entity_value'] = $membershipType1->id
;
2382 $this->createScheduleFromFixtures('sched_membership_join_2week');
2384 $this->assertCronRuns([
2386 'time' => '2012-03-29 01:00:00',
2387 'recipients' => [['test-member@example.com'], ['favorite@example.com']],
2389 'subject sched_membership_join_2week (joined March 15th, 2012)',
2390 'subject sched_membership_join_2week (joined March 15th, 2012)',
2395 // Test a reminder for the second membership type - that should include
2397 $this->fixtures
['sched_membership_start_1week']['entity_value'] = $membershipType2->id
;
2398 $this->createScheduleFromFixtures('sched_membership_start_1week');
2400 $this->assertCronRuns([
2402 'time' => '2012-03-22 01:00:00',
2403 'recipients' => [['test-member@example.com'], ['black.sheep@example.com']],
2405 'subject sched_membership_start_1week (joined March 15th, 2012)',
2406 'subject sched_membership_start_1week (joined March 15th, 2012)',
2413 * Modify the date time by the modify rule.
2415 * @param DateTime $origDateTime
2416 * @param string $modifyRule
2420 public function createModifiedDateTime(DateTime
$origDateTime, string $modifyRule): DateTime
{
2421 $newDateTime = clone($origDateTime);
2422 $newDateTime->modify($modifyRule);
2423 return $newDateTime;
2427 * Test absolute date handling for membership.
2429 * @throws \API_Exception
2430 * @throws \CRM_Core_Exception
2432 public function testMembershipScheduleWithAbsoluteDate(): void
{
2433 $membership = $this->createMembershipFromFixture('rolling_membership', 'New', [
2434 'email' => 'test-member@example.com',
2435 'location_type_id' => 1,
2438 $this->callAPISuccess('contact', 'create', array_merge($this->fixtures
['contact'], ['contact_id' => $membership->contact_id
]));
2439 $this->fixtures
['sched_membership_absolute_date']['entity_value'] = $membership->membership_type_id
;
2440 $this->createScheduleFromFixtures('sched_membership_absolute_date');
2442 $this->assertCronRuns([
2444 // Before the 24-hour mark, no email
2445 'time' => '2012-06-13 04:00:00',
2450 // On absolute date set on 2012-06-14
2451 'time' => '2012-06-14 00:00:00',
2452 'recipients' => [['test-member@example.com']],
2453 'subjects' => ['subject sched_membership_absolute_date'],
2456 // Run cron 4 hours later; first message already sent
2457 'time' => '2012-06-14 04:00:00',
2465 * @param string $fixture
2466 * Key from $this->fixtures
2467 * @param string $status
2469 * @param array $emailParams
2471 * @return \CRM_Member_DAO_Membership
2472 * @throws \API_Exception
2474 protected function createMembershipFromFixture(string $fixture, string $status, $emailParams = []): CRM_Member_DAO_Membership
{
2475 /* @var CRM_Member_DAO_Membership $membership */
2476 $membership = $this->createTestObject(
2477 'CRM_Member_DAO_Membership',
2478 array_merge($this->fixtures
[$fixture], ['status_id' => CRM_Core_PseudoConstant
::getKey('CRM_Member_BAO_Membership', 'status_id', $status)])
2481 Civi\Api4\Email
::create(FALSE)->setValues(array_merge([
2482 'contact_id' => $membership->contact_id
,
2483 'location_type_id' => 1,
2484 ], $emailParams))->execute();
2490 * Create action schedule from defined fixtures.
2492 * @param string $fixture
2493 * @param array $extraParams
2495 * @throws \CRM_Core_Exception
2497 protected function createScheduleFromFixtures(string $fixture, $extraParams = []): void
{
2498 $id = $this->callAPISuccess('ActionSchedule', 'create', array_merge($this->fixtures
[$fixture], $extraParams))['id'];
2499 $this->fixtures
[$fixture]['action_schedule_id'] = (int) $id;
2503 * @param string $activityKey
2504 * @param string $contactKey
2506 * @throws \CRM_Core_Exception
2508 protected function createActivityAndContactFromFixtures(string $activityKey = 'phone_call', string $contactKey = 'contact'): void
{
2509 $activity = $this->createTestObject('CRM_Activity_DAO_Activity', $this->fixtures
[$activityKey]);
2510 $contact = $this->callAPISuccess('contact', 'create', $this->fixtures
[$contactKey]);
2514 $source['contact_id'] = $contact['id'];
2515 $source['activity_id'] = $activity->id
;
2516 $source['record_type_id'] = 2;
2517 $activityContact = $this->createTestObject('CRM_Activity_DAO_ActivityContact', $source);
2518 $activityContact->save();