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 $customDateField = $this->callAPISuccess('CustomField', 'create', [
759 'label' => 'Test Date Field',
760 'data_type' => 'Date',
761 'html_type' => 'Select Date',
762 'date_format' => 'mm/dd/yy',
763 'custom_group_id' => $customGroup['id'],
766 $this->fixtures
['contact_custom_token'] = [
767 'id' => $customField['id'],
768 'token' => sprintf('{contact.custom_%s}', $customField['id']),
769 'name' => sprintf('custom_%s', $customField['id']),
770 'value' => 'text ' . substr(sha1(mt_rand()), 0, 7),
773 $this->fixtures
['sched_on_custom_date'] = [
774 'name' => 'sched_on_custom_date',
775 'title' => 'sched_on_custom_date',
776 'body_html' => '<p>Send reminder before 1 hour of custom date field</p>',
777 'body_text' => 'Send reminder on custom date field',
778 'subject' => 'Send reminder on custom date field',
780 'entity_value' => 'custom_' . $customDateField['id'],
781 'entity_status' => 2,
784 ['custom_' . $customDateField['id']],
787 'start_action_offset' => 1,
788 'start_action_unit' => 'hour',
789 'start_action_condition' => 'before',
790 'start_action_date' => 'date_field',
791 'record_activity' => 1,
792 'repetition_frequency_unit' => 'hour',
793 'end_frequency_unit' => 'hour',
794 'end_action' => 'before',
795 'end_date' => 'date_field',
796 'custom_field_name' => 'custom_' . $customDateField['id'],
803 * Tears down the fixture, for example, closes a network connection.
805 * This method is called after a test is executed.
807 * @throws \CRM_Core_Exception
809 public function tearDown(): void
{
811 $this->mut
->clearMessages();
814 $this->quickCleanup([
815 'civicrm_action_schedule',
816 'civicrm_action_log',
817 'civicrm_membership',
819 'civicrm_participant',
827 * Get mailer examples.
831 public function mailerExamples(): array {
834 // Some tokens - short as subject has 128char limit in DB.
835 $someTokensTmpl = implode(';;', [
836 // basic contact token
837 '{contact.display_name}',
838 // funny legacy contact token
840 // funny legacy contact token
841 '{contact.gender_id}',
844 // action-scheduler token
845 '{activity.activity_type}',
847 // Further tokens can be tested in the body text/html.
848 $manyTokensTmpl = implode(';;', [
850 '{contact.email_greeting}',
851 $this->fixtures
['contact_custom_token']['token'],
853 // Note: The behavior of domain-tokens on a scheduled reminder is undefined. All we
854 // can really do is check that it has something.
855 $someTokensExpected = 'Churmondleia Ōtākou;;Female;;Female;;[a-zA-Z0-9 ]+;;Phone Call';
856 $manyTokensExpected = sprintf('%s;;Dear Churmondleia;;%s', $someTokensExpected, $this->fixtures
['contact_custom_token']['value']);
858 // In this example, we use a lot of tokens cutting across multiple components.
860 // Schedule definition.
862 'subject' => "subj $someTokensTmpl",
863 'body_html' => "html $manyTokensTmpl",
864 'body_text' => "text $manyTokensTmpl",
866 // Assertions (regex).
868 'from_name' => '/^FIXME$/',
869 'from_email' => '/^info@EXAMPLE.ORG$/',
870 'subject' => "/^subj $someTokensExpected\$/",
871 'body_html' => "/^html $manyTokensExpected\$/",
872 'body_text' => "/^text $manyTokensExpected\$/",
876 // In this example, we customize the from address.
878 // Schedule definition.
880 'from_name' => 'Bob',
881 'from_email' => 'bob@example.org',
883 // Assertions (regex).
885 'from_name' => '/^Bob$/',
886 'from_email' => '/^bob@example.org$/',
890 // In this example, we auto-convert HTML to text
892 // Schedule definition.
894 'body_html' => '<p>Hello & stuff.</p>',
897 // Assertions (regex).
899 'body_html' => '/^' . preg_quote('<p>Hello & stuff.</p>', '/') . '/',
900 'body_text' => '/^' . preg_quote('Hello & stuff.', '/') . '/',
904 // In this example, we autoconvert HTML to text
906 // Schedule definition.
909 'body_text' => 'Hello world',
911 // Assertions (regex).
913 'body_html' => '/^--UNDEFINED--$/',
914 'body_text' => '/^Hello world$/',
922 * This generates a single mailing through the scheduled-reminder
923 * system (using an activity-reminder as a baseline) and
924 * checks that the resulting message satisfies various
925 * regular expressions.
927 * @param array $schedule
928 * Values to set/override in the schedule.
929 * Ex: array('subject' => 'Hello, {contact.first_name}!').
930 * @param array $patterns
931 * A list of regexes to compare with the actual email.
932 * Ex: array('subject' => '/^Hello, Alice!/').
933 * Keys: subject, body_text, body_html, from_name, from_email.
935 * @throws \API_Exception
936 * @throws \CRM_Core_Exception
937 * @throws \Civi\API\Exception\UnauthorizedException
938 * @dataProvider mailerExamples
940 public function testMailer(array $schedule, array $patterns): void
{
941 $this->createScheduleFromFixtures('sched_activity_1day', $schedule);
942 $activity = $this->createTestObject('CRM_Activity_DAO_Activity', $this->fixtures
['phone_call']);
943 $contact = $this->callAPISuccess('contact', 'create', array_merge(
944 $this->fixtures
['contact'],
946 $this->fixtures
['contact_custom_token']['name'] => $this->fixtures
['contact_custom_token']['value'],
951 ActivityContact
::create(FALSE)->setValues([
952 'contact_id' => $contact['id'],
953 'activity_id' => $activity->id
,
954 'record_type_id:name' => 'Activity Source',
957 CRM_Utils_Time
::setTime('2012-06-14 15:00:00');
958 $this->callAPISuccess('job', 'send_reminder');
959 $this->mut
->assertRecipients([['test-member@example.com']]);
960 foreach ($this->mut
->getAllMessages('ezc') as $message) {
961 /** @var ezcMail $message */
964 $messageArray['subject'] = $message->subject
;
965 $messageArray['from_name'] = $message->from
->name
;
966 $messageArray['from_email'] = $message->from
->email
;
967 $messageArray['body_text'] = '--UNDEFINED--';
968 $messageArray['body_html'] = '--UNDEFINED--';
970 foreach ($message->fetchParts() as $part) {
971 /** @var ezcMailText ezcMailText */
972 if ($part instanceof ezcMailText
&& $part->subType
=== 'html') {
973 $messageArray['body_html'] = $part->text
;
975 if ($part instanceof ezcMailText
&& $part->subType
=== 'plain') {
976 $messageArray['body_text'] = $part->text
;
980 foreach ($patterns as $field => $pattern) {
981 $this->assertRegExp($pattern, $messageArray[$field],
982 "Check that '$field'' matches regex. " . print_r(['expected' => $patterns, 'actual' => $messageArray], 1));
985 $this->mut
->clearMessages();
989 * Send reminder 1 hour before custom date field
991 public function testReminderWithCustomDateField(): void
{
992 $this->createScheduleFromFixtures('sched_on_custom_date');
993 $this->callAPISuccess('contact', 'create', array_merge($this->fixtures
['contact'], [$this->fixtures
['sched_on_custom_date']['custom_field_name'] => '04/06/2021']));
994 $this->assertCronRuns([
996 // Before the 24-hour mark, no email
997 'time' => '2021-04-02 04:00:00',
1002 // After the 24-hour mark, an email
1003 'time' => '2021-04-05 23:00:00',
1004 'recipients' => [['test-member@example.com']],
1005 'subjects' => ['Send reminder on custom date field'],
1008 // Run cron again; message already sent
1016 * Test calculated activity schedule.
1018 * @throws \API_Exception
1019 * @throws \CRM_Core_Exception
1021 public function testActivityDateTimeMatchNonRepeatableSchedule(): void
{
1022 $this->createScheduleFromFixtures('sched_activity_1day');
1024 $activity = $this->createTestObject('CRM_Activity_DAO_Activity', $this->fixtures
['phone_call']);
1025 $contact = $this->callAPISuccess('contact', 'create', $this->fixtures
['contact']);
1026 $activity->subject
= 'Test subject for phone_call';
1029 $source['contact_id'] = $contact['id'];
1030 $source['activity_id'] = $activity->id
;
1031 $source['record_type_id'] = 2;
1032 $activityContact = $this->createTestObject('CRM_Activity_DAO_ActivityContact', $source);
1033 $activityContact->save();
1035 $this->assertCronRuns([
1037 // Before the 24-hour mark, no email
1038 'time' => '2012-06-14 04:00:00',
1043 // After the 24-hour mark, an email
1044 'time' => '2012-06-14 15:00:00',
1045 'recipients' => [['test-member@example.com']],
1046 'subjects' => ['1-Day (non-repeating) (about Phone Call)'],
1049 // Run cron again; message already sent
1054 $activities = Activity
::get(FALSE)
1055 ->setSelect(['details'])
1056 ->addWhere('activity_type_id:name', '=', 'Reminder Sent')
1057 ->addWhere('source_record_id', '=', $activity->id
)
1059 foreach ($activities as $activityDetails) {
1060 $this->assertContains($activity->subject
, $activityDetails['details']);
1065 * Test schedule creation on repeatable schedule.
1067 * @throws \CRM_Core_Exception
1069 public function testActivityDateTimeMatchRepeatableSchedule(): void
{
1070 $this->createScheduleFromFixtures('sched_activity_1day_r');
1071 $this->createActivityAndContactFromFixtures();
1073 $this->assertCronRuns([
1075 // Before the 24-hour mark, no email
1076 'time' => '2012-06-14 04:00:00',
1081 // After the 24-hour mark, an email
1082 'time' => '2012-06-14 15:00:00',
1083 'recipients' => [['test-member@example.com']],
1084 'subjects' => ['1-Day (repeating) (about Phone Call)'],
1087 // Run cron 4 hours later; first message already sent
1088 'time' => '2012-06-14 20:00:00',
1093 // Run cron 6 hours later; send second message.
1094 'time' => '2012-06-14 21:00:01',
1095 'recipients' => [['test-member@example.com']],
1096 'subjects' => ['1-Day (repeating) (about Phone Call)'],
1102 * @throws \CRM_Core_Exception
1104 public function testActivityDateTimeMatchRepeatableScheduleOnAbsDate(): void
{
1105 $this->createScheduleFromFixtures('sched_activity_1day_r_on_abs_date');
1106 $this->createActivityAndContactFromFixtures();
1108 $this->assertCronRuns([
1110 // Before the 24-hour mark, no email
1111 'time' => '2012-06-13 04:00:00',
1116 // On absolute date set on 2012-06-14
1117 'time' => '2012-06-14 00:00:00',
1118 'recipients' => [['test-member@example.com']],
1119 'subjects' => ['1-Day (repeating) (about Phone Call)'],
1122 // Run cron 4 hours later; first message already sent
1123 'time' => '2012-06-14 04:00:00',
1128 // Run cron 6 hours later; send second message.
1129 'time' => '2012-06-14 06:00:01',
1130 'recipients' => [['test-member@example.com']],
1131 'subjects' => ['1-Day (repeating) (about Phone Call)'],
1137 * Test event with only an absolute date.
1139 * @throws \CRM_Core_Exception
1141 public function testEventNameWithAbsoluteDateAndNothingElse(): void
{
1142 $participant = $this->createTestObject('CRM_Event_DAO_Participant', array_merge($this->fixtures
['participant'], ['status_id' => 1]));
1143 $this->callAPISuccess('Email', 'create', [
1144 'contact_id' => $participant->contact_id
,
1145 'email' => 'test-event@example.com',
1147 $this->callAPISuccess('contact', 'create', array_merge($this->fixtures
['contact'], ['contact_id' => $participant->contact_id
]));
1149 $actionSchedule = $this->fixtures
['sched_event_name_1day_on_abs_date'];
1150 $actionSchedule['entity_value'] = $participant->event_id
;
1151 $this->callAPISuccess('action_schedule', 'create', $actionSchedule);
1153 $this->assertCronRuns([
1155 // Before the 24-hour mark, no email
1156 'time' => '2012-06-13 04:00:00',
1161 // On absolute date set on 2012-06-14
1162 'time' => '2012-06-14 00:00:00',
1163 'recipients' => [['test-event@example.com']],
1164 'subjects' => ['sched_event_name_1day_on_abs_date'],
1167 // Run cron 4 hours later; first message already sent
1168 'time' => '2012-06-14 04:00:00',
1176 * For contacts/members which match schedule based on join/start date,
1177 * an email should be sent.
1179 * @throws \CRM_Core_Exception
1181 public function testMembershipDateMatch(): void
{
1182 $membership = $this->createTestObject('CRM_Member_DAO_Membership', array_merge($this->fixtures
['rolling_membership'], ['status_id' => 1]));
1183 $this->callAPISuccess('Email', 'create', [
1184 'contact_id' => $membership->contact_id
,
1185 'email' => 'test-member@example.com',
1186 'location_type_id' => 1,
1190 $this->callAPISuccess('contact', 'create', array_merge($this->fixtures
['contact'], ['contact_id' => $membership->contact_id
]));
1191 $this->createScheduleFromFixtures('sched_membership_join_2week', ['entity_value' => $membership->membership_type_id
]);
1193 // start_date=2012-03-15 ; schedule is 2 weeks after join_date
1194 $this->assertCronRuns([
1196 // Before the 2-week mark, no email.
1197 'time' => '2012-03-28 01:00:00',
1202 // After the 2-week mark, send an email.
1203 'time' => '2012-03-29 01:00:00',
1204 'recipients' => [['test-member@example.com']],
1205 'subjects' => ['subject sched_membership_join_2week (joined March 15th, 2012)'],
1209 $this->createScheduleFromFixtures('sched_membership_start_1week', ['entity_value' => $membership->membership_type_id
]);
1211 // start_date=2012-03-15 ; schedule is 1 weeks after start_date
1212 $this->assertCronRuns([
1214 // Before the 2-week mark, no email.
1215 'time' => '2012-03-21 01:00:00',
1220 // After the 2-week mark, send an email.
1221 'time' => '2012-03-22 01:00:00',
1222 'recipients' => [['test-member@example.com']],
1223 'subjects' => ['subject sched_membership_start_1week (joined March 15th, 2012)'],
1229 * CRM-21675: Support parent and smart group in 'Limit to' field
1231 * @throws \CRM_Core_Exception
1232 * @throws \CiviCRM_API3_Exception
1234 public function testScheduleReminderWithParentGroup(): void
{
1235 // Contact A with birth-date at '07-07-2005' and gender - Male, later got added in smart group
1236 $this->individualCreate(['birth_date' => '20050707', 'gender_id' => 1, 'email' => 'abc@test.com']);
1237 // Contact B with birth-date at '07-07-2005', later got added in regular group
1238 $contactID2 = $this->individualCreate(['birth_date' => '20050707', 'email' => 'def@test.com'], 1);
1239 // Contact C with birth-date at '07-07-2005', but not included in any group
1240 $this->individualCreate(['birth_date' => '20050707', 'email' => 'ghi@test.com'], 2);
1242 // create regular group and add Contact B to it
1243 $groupID = $this->groupCreate();
1244 $this->callAPISuccess('GroupContact', 'Create', [
1245 'group_id' => $groupID,
1246 'contact_id' => $contactID2,
1249 // create smart group which will contain all Male contacts
1250 $smartGroupParams = ['form_values' => ['gender_id' => 1]];
1251 $smartGroupID = $this->smartGroupCreate(
1254 'name' => 'new_smart_group',
1255 'title' => 'New Smart Group',
1256 'parents' => [$groupID => 1],
1260 $actionScheduleParams = [
1261 'name' => 'sched_contact_birth_day_yesterday',
1262 'title' => 'sched_contact_birth_day_yesterday',
1263 'absolute_date' => '',
1264 'body_html' => '<p>you look like you were born yesterday!</p>',
1265 'body_text' => 'you look like you were born yesterday!',
1268 'end_frequency_interval' => '',
1269 'end_frequency_unit' => '',
1270 'entity_status' => 1,
1271 'entity_value' => 'birth_date',
1273 'group_id' => $groupID,
1277 'msg_template_id' => '',
1279 'recipient_listing' => '',
1280 'recipient_manual' => '',
1281 'record_activity' => 1,
1282 'repetition_frequency_interval' => '',
1283 'repetition_frequency_unit' => '',
1284 'start_action_condition' => 'after',
1285 'start_action_date' => 'date_field',
1286 'start_action_offset' => '1',
1287 'start_action_unit' => 'day',
1288 'subject' => 'subject sched_contact_birth_day_yesterday',
1291 // Create schedule reminder where parent group ($groupID) is selected to limit recipients,
1292 // which contain a individual contact - $contactID2 and is parent to smart group.
1293 $this->callAPISuccess('ActionSchedule', 'create', $actionScheduleParams);
1294 $this->assertCronRuns([
1296 // On the birthday, no email.
1297 'time' => '2005-07-07 01:00:00',
1301 // The next day, send an email.
1302 'time' => '2005-07-08 20:00:00',
1313 $this->groupDelete($smartGroupID);
1314 $this->groupDelete($groupID);
1318 * Test end date email sent.
1320 * For contacts/members which match schedule based on join date,
1321 * an email should be sent.
1323 * @throws \API_Exception
1324 * @throws \CRM_Core_Exception
1326 public function testMembershipJoinDateNonMatch(): void
{
1327 $this->createMembershipFromFixture('rolling_membership', '', ['email' => 'test-member@example.com']);
1328 // Add an alternative membership type, and only send messages for that type
1329 $extraMembershipType = $this->createTestObject('CRM_Member_DAO_MembershipType', []);
1330 $this->createScheduleFromFixtures('sched_membership_join_2week', ['entity_value' => $extraMembershipType->id
]);
1332 // start_date=2012-03-15 ; schedule is 2 weeks after start_date
1333 $this->assertCronRuns([
1335 // After the 2-week mark, don't send email because we have different membership type.
1336 'time' => '2012-03-29 01:00:00',
1343 * Test that the first and SECOND notifications are sent out.
1345 * @throws \API_Exception
1346 * @throws \CRM_Core_Exception
1348 public function testMembershipEndDateRepeat(): void
{
1349 // creates membership with end_date = 20120615
1350 $membership = $this->createMembershipFromFixture('rolling_membership', 'Current');
1351 $this->callAPISuccess('Email', 'create', [
1352 'contact_id' => $membership->contact_id
,
1353 'email' => 'test-member@example.com',
1355 $this->callAPISuccess('contact', 'create', array_merge($this->fixtures
['contact'], ['contact_id' => $membership->contact_id
]));
1357 $this->createScheduleFromFixtures('sched_membership_end_2month_repeat_twice_4_weeks', ['entity_value' => $membership->membership_type_id
]);
1359 // end_date=2012-06-15 ; schedule is 2 weeks before end_date
1360 $this->assertCronRuns([
1362 // After the 1-month mark, no email
1363 'time' => '2012-07-15 01:00:00',
1367 // After the 2-month mark, send an email.
1368 'time' => '2012-08-15 01:00:00',
1369 'recipients' => [['test-member@example.com']],
1372 // 4 weeks after first email send first repeat
1373 'time' => '2012-09-12 01:00:00',
1374 'recipients' => [['test-member@example.com']],
1377 // 1 week after first repeat send nothing
1378 // There was a bug where the first repeat went out and then
1379 // it would keep going out every cron run. This is to check that's
1381 'time' => '2012-09-19 01:00:00',
1385 // 4 weeks after first repeat send second repeat
1386 'time' => '2012-10-10 01:00:00',
1387 'recipients' => [['test-member@example.com']],
1390 // 4 months after membership end, send nothing
1391 'time' => '2012-10-15 01:00:00',
1395 // 5 months after membership end, send nothing
1396 'time' => '2012-11-15 01:00:00',
1403 * Test behaviour when date changes.
1405 * Test that the first notification is sent but the second is NOT sent if the end date changes in
1409 * @throws \API_Exception
1410 * @throws \CRM_Core_Exception
1412 public function testMembershipEndDateRepeatChangedEndDate_CRM_15376(): void
{
1413 // creates membership with end_date = 20120615
1414 $membership = $this->createMembershipFromFixture('rolling_membership', 'Current');
1415 $this->callAPISuccess('Email', 'create', [
1416 'contact_id' => $membership->contact_id
,
1417 'email' => 'test-member@example.com',
1419 $this->callAPISuccess('contact', 'create', array_merge($this->fixtures
['contact'], ['contact_id' => $membership->contact_id
]));
1421 $this->createScheduleFromFixtures('sched_membership_end_2month_repeat_twice_4_weeks', ['entity_value' => $membership->membership_type_id
]);
1422 // end_date=2012-06-15 ; schedule is 2 weeks before end_date
1423 $this->assertCronRuns([
1425 // After the 2-week mark, send an email.
1426 'time' => '2012-08-15 01:00:00',
1427 'recipients' => [['test-member@example.com']],
1431 // Extend membership - reminder should NOT go out.
1432 $this->callAPISuccess('membership', 'create', ['id' => $membership->id
, 'end_date' => '2014-01-01']);
1433 $this->assertCronRuns([
1435 // After the 2-week mark, send an email.
1436 'time' => '2012-09-12 01:00:00',
1443 * Test membership end date email sends.
1445 * For contacts/members which match schedule based on end date,
1446 * an email should be sent.
1448 * @throws \API_Exception
1449 * @throws \CRM_Core_Exception
1451 public function testMembershipEndDateMatch(): void
{
1452 // creates membership with end_date = 20120615
1453 $membership = $this->createMembershipFromFixture('rolling_membership', 'Current');
1454 $this->callAPISuccess('Email', 'create', [
1455 'contact_id' => $membership->contact_id
,
1456 'email' => 'test-member@example.com',
1458 $this->callAPISuccess('contact', 'create', array_merge($this->fixtures
['contact'], ['contact_id' => $membership->contact_id
]));
1460 $this->createScheduleFromFixtures('sched_membership_end_2week', ['entity_value' => $membership->membership_type_id
]);
1462 // end_date=2012-06-15 ; schedule is 2 weeks before end_date
1463 $this->assertCronRuns([
1465 // Before the 2-week mark, no email.
1466 'time' => '2012-05-31 01:00:00',
1470 // After the 2-week mark, send an email.
1471 'time' => '2012-06-01 01:00:00',
1472 'recipients' => [['test-member@example.com']],
1475 // After the email is sent, another one is not sent
1476 'time' => '2012-06-01 02:00:00',
1481 // Now suppose user has renewed for rolling membership after 3 months, so upcoming assertion is written
1482 // to ensure that new reminder is sent 2 week before the new end_date i.e. '2012-09-15'
1483 $membership->end_date
= '2012-09-15';
1484 $membership->save();
1486 //change the email id of chosen membership contact to assert
1487 //recipient of not the previously sent mail but the new one
1488 $result = $this->callAPISuccess('Email', 'create', [
1490 'contact_id' => $membership->contact_id
,
1491 'email' => 'member2@example.com',
1493 $this->assertAPISuccess($result);
1495 // end_date=2012-09-15 ; schedule is 2 weeks before end_date
1496 $this->assertCronRuns([
1498 // Before the 2-week mark, no email
1499 'time' => '2012-08-31 01:00:00',
1503 // After the 2-week mark, send an email
1504 'time' => '2012-09-01 01:00:00',
1505 'recipients' => [['member2@example.com']],
1508 // After the email is sent, another one is not sent
1509 'time' => '2012-09-01 02:00:00',
1514 $membership->end_date
= '2012-12-15';
1515 $membership->save();
1516 // end_date=2012-12-15 ; schedule is 2 weeks before end_date
1517 $this->assertCronRuns([
1519 // Before the 2-week mark, no email
1520 'time' => '2012-11-30 01:00:00',
1524 // After the 2-week mark, send an email
1525 'time' => '2012-12-01 01:00:00',
1526 'recipients' => [['member2@example.com']],
1529 // After the email is sent, another one is not sent
1530 'time' => '2012-12-01 02:00:00',
1538 * @param array $contactFixture
1539 * @param int $membershipTypeId
1541 * @return array|NULL|object
1542 * @throws \CRM_Core_Exception
1544 public function createMembershipAndContact(array $contactFixture, int $membershipTypeId) {
1545 $result = $this->callAPISuccess('contact', 'create', $contactFixture);
1546 $contact = $result['values'][$result['id']];
1549 'contact_id' => $contact['id'],
1550 'membership_type_id' => $membershipTypeId,
1551 'owner_membership_id' => 'NULL',
1553 $params = array_merge($this->fixtures
['rolling_membership'], $params);
1554 return $this->createTestObject('CRM_Member_DAO_Membership', $params);
1558 * This test is very similar to testMembershipEndDateMatch, but it adds
1559 * another contact because there was a bug in
1560 * RecipientBuilder::buildRelFirstPass where it was only sending the
1561 * reminder for the first contact returned in a query for renewed
1562 * memberships. Other contacts wouldn't get the mail.
1564 * @throws \CRM_Core_Exception
1566 public function testMultipleMembershipEndDateMatch(): void
{
1567 $membershipTypeId = $this->membershipTypeCreate($this->fixtures
['rolling_membership']['membership_type_id']);
1568 $membershipOne = $this->createMembershipAndContact($this->fixtures
['contact'], $membershipTypeId);
1569 $membershipTwo = $this->createMembershipAndContact($this->fixtures
['contact_2'], $membershipTypeId);
1570 $this->createScheduleFromFixtures('sched_membership_end_2week', ['entity_value' => $membershipTypeId]);
1572 // end_date=2012-06-15 ; schedule is 2 weeks before end_date
1573 $this->assertCronRuns([
1575 // Before the 2-week mark, no email.
1576 'time' => '2012-05-31 01:00:00',
1580 // After the 2-week mark, send emails.
1581 'time' => '2012-06-01 01:00:00',
1583 ['test-member@example.com'],
1584 ['test-contact-2@example.com'],
1588 // After the email is sent, another one is not sent
1589 'time' => '2012-06-01 02:00:00',
1594 // Now suppose user has renewed for rolling membership after 3 months, so upcoming assertion is written
1595 // to ensure that new reminder is sent 2 week before the new end_date i.e. '2012-09-15'
1596 $membershipOne->end_date
= '2012-09-15';
1597 $membershipOne->save();
1598 $membershipTwo->end_date
= '2012-09-15';
1599 $membershipTwo->save();
1601 // end_date=2012-09-15 ; schedule is 2 weeks before end_date
1602 $this->assertCronRuns([
1604 // Before the 2-week mark, no email
1605 'time' => '2012-08-31 01:00:00',
1609 // After the 2-week mark, send an email
1610 'time' => '2012-09-01 01:00:00',
1612 ['test-member@example.com'],
1613 ['test-contact-2@example.com'],
1617 // After the email is sent, another one is not sent
1618 'time' => '2012-06-01 02:00:00',
1625 * Test membership end date email.
1627 * For contacts/members which match schedule based on end date,
1628 * an email should be sent.
1630 * @throws \API_Exception
1631 * @throws \CRM_Core_Exception
1633 public function testMembershipEndDateNoMatch(): void
{
1634 // creates membership with end_date = 20120615
1635 $membership = $this->createMembershipFromFixture('rolling_membership', 'Grace');
1636 $this->callAPISuccess('Email', 'create', [
1637 'contact_id' => $membership->contact_id
,
1638 'email' => 'test-member@example.com',
1640 $this->callAPISuccess('contact', 'create', array_merge($this->fixtures
['contact'], ['contact_id' => $membership->contact_id
]));
1641 $this->createScheduleFromFixtures('sched_membership_end_2month', ['entity_value' => $membership->membership_type_id
]);
1643 // end_date=2012-06-15 ; schedule is 2 weeks before end_date
1644 $this->assertCronRuns([
1646 // Before the 2-week mark, no email.
1647 'time' => '2012-05-31 01:00:00',
1651 // After the 2-week mark, no email
1652 'time' => '2013-05-01 01:00:00',
1659 * @throws \CRM_Core_Exception
1661 public function testContactBirthDateNoAnniversary(): void
{
1662 $contact = $this->callAPISuccess('Contact', 'create', $this->fixtures
['contact_birthdate']);
1663 $this->_testObjects
['CRM_Contact_DAO_Contact'][] = $contact['id'];
1664 $this->createScheduleFromFixtures('sched_contact_birth_day_yesterday');
1665 $this->assertCronRuns([
1667 // On the birthday, no email.
1668 'time' => '2005-07-07 01:00:00',
1672 // The next day, send an email.
1673 'time' => '2005-07-08 20:00:00',
1674 'recipients' => [['test-birth_day@example.com']],
1680 * @throws \CRM_Core_Exception
1682 public function testContactBirthDateAnniversary(): 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_birth_day_anniversary');
1686 $this->assertCronRuns([
1688 // On some random day, no email.
1689 'time' => '2014-03-07 01:00:00',
1693 // On the eve of their 9th birthday, send an email.
1694 'time' => '2014-07-06 20:00:00',
1695 'recipients' => [['test-birth_day@example.com']],
1701 * @throws \CRM_Core_Exception
1703 public function testContactCustomDateNoAnniversary(): void
{
1705 'title' => 'Test_Group',
1706 'name' => 'test_group',
1707 'extends' => ['Individual'],
1708 'style' => 'Inline',
1709 'is_multiple' => FALSE,
1712 $createGroup = $this->callAPISuccess('custom_group', 'create', $group);
1714 'label' => 'Graduation',
1715 'data_type' => 'Date',
1716 'html_type' => 'Select Date',
1717 'custom_group_id' => $createGroup['id'],
1719 $createField = $this->callAPISuccess('custom_field', 'create', $field);
1720 $contactParams = $this->fixtures
['contact'];
1721 $contactParams["custom_{$createField['id']}"] = '2013-12-16';
1722 $contact = $this->callAPISuccess('Contact', 'create', $contactParams);
1723 $this->_testObjects
['CRM_Contact_DAO_Contact'][] = $contact['id'];
1724 $this->createScheduleFromFixtures('sched_contact_grad_tomorrow', ['entity_value' => "custom_{$createField['id']}"]);
1725 $this->assertCronRuns([
1727 // On some random day, no email.
1728 'time' => '2014-03-07 01:00:00',
1732 // On the eve of their graduation, send an email.
1733 'time' => '2013-12-15 20:00:00',
1734 'recipients' => [['test-member@example.com']],
1737 $this->callAPISuccess('custom_group', 'delete', ['id' => $createGroup['id']]);
1741 * @throws \CRM_Core_Exception
1743 public function testContactCreatedNoAnniversary(): void
{
1744 $contact = $this->callAPISuccess('Contact', 'create', $this->fixtures
['contact_birthdate']);
1745 $this->_testObjects
['CRM_Contact_DAO_Contact'][] = $contact['id'];
1746 $this->createScheduleFromFixtures('sched_contact_created_yesterday');
1747 $this->assertCronRuns([
1749 // On the date created, no email.
1750 'time' => $contact['values'][$contact['id']]['created_date'],
1754 // The next day, send an email.
1755 'time' => date('Y-m-d H:i:s', strtotime($contact['values'][$contact['id']]['created_date'] . ' +1 day')),
1756 'recipients' => [['test-birth_day@example.com'], ['fixme.domainemail@example.org'], ['domainemail2@example.org']],
1762 * Test the impact of changing the anniversary.
1764 * @throws \CRM_Core_Exception
1766 public function testContactModifiedAnniversary(): void
{
1767 $contact = $this->callAPISuccess('Contact', 'create', $this->fixtures
['contact_birthdate']);
1768 $this->_testObjects
['CRM_Contact_DAO_Contact'][] = $contact['id'];
1769 $modifiedDate = $this->callAPISuccess('Contact', 'getvalue', ['id' => $contact['id'], 'return' => 'modified_date']);
1770 $this->createScheduleFromFixtures('sched_contact_mod_anniversary');
1771 $this->assertCronRuns([
1773 // On some random day, no email.
1774 'time' => date('Y-m-d H:i:s', strtotime($contact['values'][$contact['id']]['modified_date'] . ' -60 days')),
1778 // On the eve of 3 years after they were modified, send an email.
1779 'time' => date('Y-m-d H:i:s', strtotime($modifiedDate . ' +3 years -1 day')),
1780 'recipients' => [['test-birth_day@example.com'], ['fixme.domainemail@example.org'], ['domainemail2@example.org']],
1786 * Check that limit_to + an empty recipients doesn't sent to multiple contacts.
1788 * @throws \API_Exception
1789 * @throws \CRM_Core_Exception
1791 public function testMembershipLimitToNone(): void
{
1792 // creates membership with end_date = 20120615
1793 $membership = $this->createMembershipFromFixture('rolling_membership', 'Current');
1794 $result = $this->callAPISuccess('Email', 'create', [
1795 'contact_id' => $membership->contact_id
,
1796 'email' => 'member@example.com',
1798 $this->callAPISuccess('contact', 'create', array_merge($this->fixtures
['contact'], ['contact_id' => $membership->contact_id
]));
1799 $this->callAPISuccess('contact', 'create', ['email' => 'b@c.com', 'contact_type' => 'Individual']);
1801 $this->assertAPISuccess($result);
1803 $this->createScheduleFromFixtures('sched_membership_end_limit_to_none', ['entity_value' => $membership->membership_type_id
]);
1805 // end_date=2012-06-15 ; schedule is 2 weeks before end_date
1806 $this->assertCronRuns([
1808 // Before the 2-week mark, no email.
1809 'time' => '2012-05-31 01:00:00',
1816 * Test handling of reference date for memberships.
1818 * @throws \API_Exception
1819 * @throws \CRM_Core_Exception
1821 public function testMembershipWithReferenceDate(): void
{
1822 $membership = $this->createMembershipFromFixture('rolling_membership', 'Current', ['email' => 'member@example.com']);
1823 $this->callAPISuccess('contact', 'create', array_merge($this->fixtures
['contact'], ['contact_id' => $membership->contact_id
]));
1825 $this->createScheduleFromFixtures('sched_membership_join_2week', ['entity_value' => $membership->membership_type_id
]);
1827 // start_date=2012-03-15 ; schedule is 2 weeks after start_date
1828 $this->assertCronRuns([
1830 // After the 2-week mark, send an email
1831 'time' => '2012-03-29 01:00:00',
1832 'recipients' => [['member@example.com']],
1835 // After the 2-week 1day mark, don't send an email
1836 'time' => '2012-03-30 01:00:00',
1841 //check if reference date is set to membership's join date
1842 //as per the action_start_date chosen for current schedule reminder
1843 $this->assertEquals('2012-03-15 00:00:00',
1844 CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_ActionLog', $membership->contact_id
, 'reference_date', 'contact_id')
1847 //change current membership join date that may signifies as membership renewal activity
1848 $membership->join_date
= '2012-03-29';
1849 $membership->save();
1851 $this->assertCronRuns([
1853 // After the 13 days of the changed join date 2012-03-29, don't send an email
1854 'time' => '2012-04-11 01:00:00',
1858 // After the 2-week of the changed join date 2012-03-29, send an email
1859 'time' => '2012-04-12 01:00:00',
1860 'recipients' => [['member@example.com']],
1863 $this->assertCronRuns([
1865 // It should not re-send on the same day
1866 'time' => '2012-04-12 01:00:00',
1873 * Test multiple membership reminder.
1875 * @throws \API_Exception
1876 * @throws \CRM_Core_Exception
1878 public function testMembershipOnMultipleReminder(): void
{
1879 $membership = $this->createMembershipFromFixture('rolling_membership', 'Current', ['email' => 'member@example.com']);
1880 $this->callAPISuccess('contact', 'create', array_merge($this->fixtures
['contact'], ['contact_id' => $membership->contact_id
]));
1882 // Send email 2 weeks before end_date
1883 $actionScheduleBefore = $this->fixtures
['sched_membership_end_2week'];
1884 // Send email on end_date/expiry date
1885 $actionScheduleOn = $this->fixtures
['sched_on_membership_end_date'];
1886 // Send email 1 day after end_date/grace period
1887 $actionScheduleAfter = $this->fixtures
['sched_after_1day_membership_end_date'];
1888 $actionScheduleBefore['entity_value'] = $actionScheduleOn['entity_value'] = $actionScheduleAfter['entity_value'] = $membership->membership_type_id
;
1889 foreach (['actionScheduleBefore', 'actionScheduleOn', 'actionScheduleAfter'] as $value) {
1890 $
$value = CRM_Core_BAO_ActionSchedule
::add($
$value);
1893 $this->assertCronRuns(
1896 // 1day 2weeks before membership end date(MED), don't send mail
1897 'time' => '2012-05-31 01:00:00',
1901 // 2 weeks before MED, send an email
1902 'time' => '2012-06-01 01:00:00',
1903 'recipients' => [['member@example.com']],
1906 // 1day before MED, don't send mail
1907 'time' => '2012-06-14 01:00:00',
1911 // On MED, send an email
1912 'time' => '2012-06-15 00:00:00',
1913 'recipients' => [['member@example.com']],
1916 // After 1day of MED, send an email
1917 'time' => '2012-06-16 01:00:00',
1918 'recipients' => [['member@example.com']],
1921 // After 1day 1min of MED, don't send an email
1922 'time' => '2012-06-17 00:01:00',
1928 // Assert the timestamp as of when the emails of respective three reminders as configured
1929 // 2 weeks before, on and 1 day after MED, are sent
1930 $this->assertApproxEquals(
1931 strtotime('2012-06-01 01:00:00'),
1932 strtotime(CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_ActionLog', $actionScheduleBefore->id
, 'action_date_time', 'action_schedule_id', TRUE)),
1933 // Variation in test execution time.
1936 $this->assertApproxEquals(
1937 strtotime('2012-06-15 00:00:00'),
1938 strtotime(CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_ActionLog', $actionScheduleOn->id
, 'action_date_time', 'action_schedule_id', TRUE)),
1939 // Variation in test execution time.
1942 $this->assertApproxEquals(
1943 strtotime('2012-06-16 01:00:00'),
1944 strtotime(CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_ActionLog', $actionScheduleAfter->id
, 'action_date_time', 'action_schedule_id', TRUE)),
1945 // Variation in test execution time.
1949 //extend MED to 2 weeks after the current MED (that may signifies as membership renewal activity)
1950 // and lets assert as of when the new set of reminders will be sent against their respective Schedule Reminders(SR)
1951 $membership->end_date
= '2012-06-20';
1952 $membership->save();
1954 $this->callAPISuccess('Contact', 'get', ['id' => $membership->contact_id
]);
1955 $this->assertCronRuns(
1958 // 1day 2weeks before membership end date(MED), don't send mail
1959 'time' => '2012-06-05 01:00:00',
1963 // 2 weeks before MED, send an email
1964 'time' => '2012-06-06 01:00:00',
1965 'recipients' => [['member@example.com']],
1968 // 1day before MED, don't send mail
1969 'time' => '2012-06-19 01:00:00',
1973 // On MED, send an email
1974 'time' => '2012-06-20 00:00:00',
1975 'recipients' => [['member@example.com']],
1978 // After 1day of MED, send an email
1979 'time' => '2012-06-21 01:00:00',
1980 'recipients' => [['member@example.com']],
1983 // After 1day 1min of MED, don't send an email
1984 'time' => '2012-07-21 00:01:00',
1991 * Test reminders sent on custom data anniversary.
1993 * @throws \API_Exception
1994 * @throws \CRM_Core_Exception
1996 public function testContactCustomDate_Anniversary(): void
{
1997 $this->createCustomGroupWithFieldOfType([], 'date');
1998 $contactParams = $this->fixtures
['contact'];
1999 $contactParams[$this->getCustomFieldName('date')] = '2013-12-16';
2000 $contact = $this->callAPISuccess('Contact', 'create', $contactParams);
2001 $this->_testObjects
['CRM_Contact_DAO_Contact'][] = $contact['id'];
2002 $this->fixtures
['sched_contact_grad_anniversary']['entity_value'] = $this->getCustomFieldName('date');
2003 $this->createScheduleFromFixtures('sched_contact_grad_anniversary');
2005 $this->assertCronRuns([
2007 // On some random day, no email.
2008 'time' => '2014-03-07 01:00:00',
2012 // A week after their 5th anniversary of graduation, send an email.
2013 'time' => '2018-12-23 20:00:00',
2014 'recipients' => [['test-member@example.com']],
2020 * Test sched reminder set via registration date.
2022 * @throws \CRM_Core_Exception
2023 * @throws \CiviCRM_API3_Exception
2025 public function testEventTypeRegistrationDate(): void
{
2026 $contact = $this->individualCreate(['email' => 'test-event@example.com']);
2027 //Add it as a participant to an event ending registration - 7 days from now.
2029 'start_date' => date('Ymd', strtotime('-5 day')),
2030 'end_date' => date('Ymd', strtotime('+7 day')),
2031 'registration_start_date' => date('Ymd', strtotime('-5 day')),
2032 'registration_end_date' => date('Ymd', strtotime('+7 day')),
2034 $event = $this->eventCreate($params);
2035 $this->participantCreate(['contact_id' => $contact, 'event_id' => $event['id']]);
2037 //Create a scheduled reminder to send email 7 days before registration date.
2038 $actionSchedule = $this->fixtures
['sched_event_type_start_1week_before'];
2039 $actionSchedule['start_action_offset'] = 7;
2040 $actionSchedule['start_action_unit'] = 'day';
2041 $actionSchedule['start_action_date'] = 'registration_end_date';
2042 $actionSchedule['entity_value'] = $event['values'][$event['id']]['event_type_id'];
2043 $actionSchedule['entity_status'] = $this->callAPISuccessGetValue('ParticipantStatusType', [
2045 'name' => 'Attended',
2047 $actionSched = $this->callAPISuccess('action_schedule', 'create', $actionSchedule);
2048 //Run the cron and verify if an email was sent.
2049 $this->assertCronRuns([
2051 'time' => date('Y-m-d'),
2052 'recipients' => [['test-event@example.com']],
2058 'email' => 'test-event2@example.com',
2060 $contact2 = $this->individualCreate($contactParams);
2061 //Create an event with registration end date = 2 week from now.
2062 $params['end_date'] = date('Ymd', strtotime('+2 week'));
2063 $params['registration_end_date'] = date('Ymd', strtotime('+2 week'));
2064 $event2 = $this->eventCreate($params);
2065 $this->participantCreate(['contact_id' => $contact2, 'event_id' => $event2['id']]);
2067 //Assert there is no reminder sent to the contact.
2068 $this->assertCronRuns([
2070 'time' => date('Y-m-d'),
2075 //Modify the sched reminder to be sent 2 week from registration end date.
2076 $this->callAPISuccess('action_schedule', 'create', [
2077 'id' => $actionSched['id'],
2078 'start_action_offset' => 2,
2079 'start_action_unit' => 'week',
2082 //Contact should receive the reminder now.
2083 $this->assertCronRuns([
2085 'time' => date('Y-m-d'),
2086 'recipients' => [['test-event2@example.com']],
2092 * Test sched reminder set via start date.
2094 * @throws \CRM_Core_Exception
2096 public function testEventTypeStartDate(): void
{
2097 // Create event+participant with start_date = 20120315, end_date = 20120615.
2098 $participant = $this->createTestObject('CRM_Event_DAO_Participant', array_merge($this->fixtures
['participant'], ['status_id' => 2]));
2099 $this->callAPISuccess('Email', 'create', [
2100 'contact_id' => $participant->contact_id
,
2101 'email' => 'test-event@example.com',
2103 $this->callAPISuccess('contact', 'create', array_merge($this->fixtures
['contact'], ['contact_id' => $participant->contact_id
]));
2105 $actionSchedule = $this->fixtures
['sched_event_type_start_1week_before'];
2106 $actionSchedule['entity_value'] = CRM_Core_DAO
::getFieldValue('CRM_Event_DAO_Event', $participant->event_id
, 'event_type_id');
2107 $this->callAPISuccess('action_schedule', 'create', $actionSchedule);
2109 //echo "CREATED\n"; ob_flush(); sleep(20);
2111 // end_date=2012-06-15 ; schedule is 2 weeks before end_date
2112 $this->assertCronRuns([
2115 'time' => '2012-03-02 01:00:00',
2120 'time' => '2012-03-08 01:00:00',
2121 'recipients' => [['test-event@example.com']],
2124 // And then nothing else
2125 'time' => '2012-03-16 01:00:00',
2132 * Test schedule on event end date.
2134 * @throws \CRM_Core_Exception
2136 public function testEventTypeEndDateRepeat(): void
{
2137 // Create event+participant with start_date = 20120315, end_date = 20120615.
2138 $participant = $this->createTestObject('CRM_Event_DAO_Participant', array_merge($this->fixtures
['participant'], ['status_id' => 2]));
2139 $this->callAPISuccess('Email', 'create', [
2140 'contact_id' => $participant->contact_id
,
2141 'email' => 'test-event@example.com',
2143 $this->callAPISuccess('contact', 'create', array_merge($this->fixtures
['contact'], ['contact_id' => $participant->contact_id
]));
2145 $actionSchedule = $this->fixtures
['sched_event_type_end_2month_repeat_twice_2_weeks'];
2146 $actionSchedule['entity_value'] = CRM_Core_DAO
::getFieldValue('CRM_Event_DAO_Event', $participant->event_id
, 'event_type_id');
2147 $this->callAPISuccess('action_schedule', 'create', $actionSchedule);
2149 $this->assertCronRuns([
2152 'time' => '2012-08-13 01:00:00',
2156 // After the 2-month mark, send an email.
2157 'time' => '2012-08-16 01:00:00',
2158 'recipients' => [['test-event@example.com']],
2161 // After 2 months and 1 week, don't repeat yet.
2162 'time' => '2012-08-23 02:00:00',
2166 // After 2 months and 2 weeks
2167 'time' => '2012-08-30 02:00:00',
2168 'recipients' => [['test-event@example.com']],
2171 // After 2 months and 4 week
2172 'time' => '2012-09-13 02:00:00',
2173 'recipients' => [['test-event@example.com']],
2176 // After 2 months and 6 weeks
2177 'time' => '2012-09-27 01:00:00',
2184 * Run a series of cron jobs and make an assertion about email deliveries.
2186 * @param array $cronRuns
2187 * array specifying when to run cron and what messages to expect; each item is an array with keys:
2188 * - time: string, e.g. '2012-06-15 21:00:01'
2189 * - recipients: array(array(string)), list of email addresses which should receive messages
2191 * @throws \CRM_Core_Exception
2192 * @noinspection DisconnectedForeachInstructionInspection
2194 public function assertCronRuns(array $cronRuns): void
{
2195 foreach ($cronRuns as $cronRun) {
2196 CRM_Utils_Time
::setTime($cronRun['time']);
2197 $this->callAPISuccess('job', 'send_reminder', []);
2198 $this->mut
->assertRecipients($cronRun['recipients']);
2199 if (array_key_exists('subjects', $cronRun)) {
2200 $this->mut
->assertSubjects($cronRun['subjects']);
2202 $this->mut
->clearMessages();
2209 * (DAO_Name => array(int)) List of items to garbage-collect during tearDown
2211 private $_testObjects;
2214 * Sets up the fixture, for example, opens a network connection.
2216 * This method is called before a test is executed.
2218 protected function _setUp(): void
{
2219 $this->_testObjects
= [];
2223 * Tears down the fixture, for example, closes a network connection.
2225 * This method is called after a test is executed.
2227 protected function _tearDown(): void
{
2229 $this->deleteTestObjects();
2233 * This is a wrapper for CRM_Core_DAO::createTestObject which tracks
2234 * created entities and provides for brainless cleanup.
2236 * @see CRM_Core_DAO::createTestObject
2239 * @param array $params
2240 * @param int $numObjects
2241 * @param bool $createOnly
2243 * @return array|NULL|object
2245 public function createTestObject($daoName, $params = [], $numObjects = 1, $createOnly = FALSE) {
2246 $objects = CRM_Core_DAO
::createTestObject($daoName, $params, $numObjects, $createOnly);
2247 if (is_array($objects)) {
2248 $this->registerTestObjects($objects);
2251 $this->registerTestObjects([$objects]);
2257 * @param array $objects
2258 * DAO or BAO objects.
2260 public function registerTestObjects(array $objects): void
{
2261 //if (is_object($objects)) {
2262 // $objects = array($objects);
2264 foreach ($objects as $object) {
2265 $daoName = str_replace('_BAO_', '_DAO_', get_class($object));
2266 $this->_testObjects
[$daoName][] = $object->id
;
2270 public function deleteTestObjects(): void
{
2271 // Note: You might argue that the FK relations between test
2272 // objects could make this problematic; however, it should
2273 // behave intuitively as long as we mentally split our
2274 // test-objects between the "manual/primary records"
2275 // and the "automatic/secondary records"
2276 foreach ($this->_testObjects
as $daoName => $daoIds) {
2277 foreach ($daoIds as $daoId) {
2278 CRM_Core_DAO
::deleteTestObjects($daoName, ['id' => $daoId]);
2281 $this->_testObjects
= [];
2285 * Test that the various repetition units work correctly.
2287 * @see https://issues.civicrm.org/jira/browse/CRM-17028
2288 * @throws \CRM_Core_Exception
2290 public function testRepetitionFrequencyUnit(): void
{
2291 $membershipTypeParams = [
2292 'duration_interval' => '1',
2293 'duration_unit' => 'year',
2295 'period_type' => 'rolling',
2297 $membershipType = $this->createTestObject('CRM_Member_DAO_MembershipType', $membershipTypeParams);
2298 $interval_units = ['hour', 'day', 'week', 'month', 'year'];
2299 foreach ($interval_units as $interval_unit) {
2300 $membershipEndDate = DateTime
::createFromFormat('Y-m-d H:i:s', '2013-03-15 00:00:00');
2302 'contact_type' => 'Individual',
2303 'first_name' => 'Test',
2304 'last_name' => "Interval $interval_unit",
2307 $contact = $this->createTestObject('CRM_Contact_DAO_Contact', $contactParams);
2309 'contact_id' => $contact->id
,
2311 'email' => "test-member-{$interval_unit}@example.com",
2312 'location_type_id' => 1,
2314 $this->createTestObject('CRM_Core_DAO_Email', $emailParams);
2315 $membershipParams = [
2316 'membership_type_id' => $membershipType->id
,
2317 'contact_id' => $contact->id
,
2318 'join_date' => '20120315',
2319 'start_date' => '20120315',
2320 'end_date' => '20130315',
2324 $membershipParams['status-id'] = 1;
2325 $membership = $this->createTestObject('CRM_Member_DAO_Membership', $membershipParams);
2326 $actionScheduleParams = $this->fixtures
['sched_on_membership_end_date_repeat_interval'];
2327 $actionScheduleParams['entity_value'] = $membershipType->id
;
2328 $actionScheduleParams['repetition_frequency_unit'] = $interval_unit;
2329 $actionScheduleParams['repetition_frequency_interval'] = 2;
2330 $actionSchedule = CRM_Core_BAO_ActionSchedule
::add($actionScheduleParams);
2331 $beforeEndDate = $this->createModifiedDateTime($membershipEndDate, '-1 day');
2332 $beforeFirstUnit = $this->createModifiedDateTime($membershipEndDate, "+1 $interval_unit");
2333 $afterFirstUnit = $this->createModifiedDateTime($membershipEndDate, "+2 $interval_unit");
2336 'time' => $beforeEndDate->format('Y-m-d H:i:s'),
2340 'time' => $membershipEndDate->format('Y-m-d H:i:s'),
2341 'recipients' => [["test-member-{$interval_unit}@example.com"]],
2344 'time' => $beforeFirstUnit->format('Y-m-d H:i:s'),
2348 'time' => $afterFirstUnit->format('Y-m-d H:i:s'),
2349 'recipients' => [["test-member-{$interval_unit}@example.com"]],
2352 $this->assertCronRuns($cronRuns);
2353 $actionSchedule->delete();
2354 $membership->delete();
2359 * Inherited members without permission to edit the main member contact should
2360 * not get reminders.
2362 * However, just because a contact inherits one membership doesn't mean
2363 * reminders for other memberships should be suppressed.
2367 * @throws \CRM_Core_Exception
2369 public function testInheritedMembershipPermissions(): void
{
2370 // Set up common parameters for memberships.
2371 $membershipParams = $this->fixtures
['rolling_membership'];
2372 $membershipParams['status_id'] = 1;
2374 $membershipParams['membership_type_id']['relationship_type_id'] = 1;
2375 $membershipParams['membership_type_id']['relationship_direction'] = 'b_a';
2376 $membershipType1 = $this->createTestObject('CRM_Member_DAO_MembershipType', $membershipParams['membership_type_id']);
2378 // We'll create a new membership type that can be held at the same time as
2380 $membershipParams['membership_type_id']['relationship_type_id'] = 'NULL';
2381 $membershipParams['membership_type_id']['relationship_direction'] = 'NULL';
2382 $membershipType2 = $this->createTestObject('CRM_Member_DAO_MembershipType', $membershipParams['membership_type_id']);
2384 // Create the parent membership and contact
2385 $membershipParams['membership_type_id'] = $membershipType1->id
;
2386 $mainMembership = $this->createTestObject('CRM_Member_DAO_Membership', $membershipParams);
2389 'contact_type' => 'Individual',
2390 'first_name' => 'Mom',
2391 'last_name' => 'Rel',
2394 $this->createTestObject('CRM_Contact_DAO_Contact', array_merge($contactParams, ['id' => $mainMembership->contact_id
]));
2397 'contact_id' => $mainMembership->contact_id
,
2398 'email' => 'test-member@example.com',
2399 'location_type_id' => 1,
2402 $this->createTestObject('CRM_Core_DAO_Email', $emailParams);
2404 // Set up contacts and emails for the two children
2405 $contactParams['first_name'] = 'Favorite';
2406 $permChild = $this->createTestObject('CRM_Contact_DAO_Contact', $contactParams);
2407 $emailParams['email'] = 'favorite@example.com';
2408 $emailParams['contact_id'] = $permChild->id
;
2409 $this->createTestObject('CRM_Core_DAO_Email', $emailParams);
2411 $contactParams['first_name'] = 'Black Sheep';
2412 $nonPermChild = $this->createTestObject('CRM_Contact_DAO_Contact', $contactParams);
2413 $emailParams['email'] = 'black.sheep@example.com';
2414 $emailParams['contact_id'] = $nonPermChild->id
;
2415 $this->createTestObject('CRM_Core_DAO_Email', $emailParams);
2417 // Each child gets a relationship, one with permission to edit the parent. This
2418 // will trigger inherited memberships for the first membership type
2420 'relationship_type_id' => 1,
2421 'contact_id_a' => $nonPermChild->id
,
2422 'contact_id_b' => $mainMembership->contact_id
,
2425 $this->callAPISuccess('relationship', 'create', $relParams);
2427 $relParams['contact_id_a'] = $permChild->id
;
2428 $relParams['is_permission_a_b'] = CRM_Contact_BAO_Relationship
::EDIT
;
2429 $this->callAPISuccess('relationship', 'create', $relParams);
2431 // Mom and Black Sheep get their own memberships of the second type.
2432 $membershipParams['membership_type_id'] = $membershipType2->id
;
2433 $membershipParams['owner_membership_id'] = 'NULL';
2434 $membershipParams['contact_id'] = $mainMembership->contact_id
;
2435 $this->createTestObject('CRM_Member_DAO_Membership', $membershipParams);
2437 $membershipParams['contact_id'] = $nonPermChild->id
;
2438 $this->createTestObject('CRM_Member_DAO_Membership', $membershipParams);
2440 // Test a reminder for the first membership type - that should exclude Black
2442 $this->fixtures
['sched_membership_join_2week']['entity_value'] = $membershipType1->id
;
2443 $this->createScheduleFromFixtures('sched_membership_join_2week');
2445 $this->assertCronRuns([
2447 'time' => '2012-03-29 01:00:00',
2448 'recipients' => [['test-member@example.com'], ['favorite@example.com']],
2450 'subject sched_membership_join_2week (joined March 15th, 2012)',
2451 'subject sched_membership_join_2week (joined March 15th, 2012)',
2456 // Test a reminder for the second membership type - that should include
2458 $this->fixtures
['sched_membership_start_1week']['entity_value'] = $membershipType2->id
;
2459 $this->createScheduleFromFixtures('sched_membership_start_1week');
2461 $this->assertCronRuns([
2463 'time' => '2012-03-22 01:00:00',
2464 'recipients' => [['test-member@example.com'], ['black.sheep@example.com']],
2466 'subject sched_membership_start_1week (joined March 15th, 2012)',
2467 'subject sched_membership_start_1week (joined March 15th, 2012)',
2474 * Modify the date time by the modify rule.
2476 * @param DateTime $origDateTime
2477 * @param string $modifyRule
2481 public function createModifiedDateTime(DateTime
$origDateTime, string $modifyRule): DateTime
{
2482 $newDateTime = clone($origDateTime);
2483 $newDateTime->modify($modifyRule);
2484 return $newDateTime;
2488 * Test absolute date handling for membership.
2490 * @throws \API_Exception
2491 * @throws \CRM_Core_Exception
2493 public function testMembershipScheduleWithAbsoluteDate(): void
{
2494 $membership = $this->createMembershipFromFixture('rolling_membership', 'New', [
2495 'email' => 'test-member@example.com',
2496 'location_type_id' => 1,
2499 $this->callAPISuccess('contact', 'create', array_merge($this->fixtures
['contact'], ['contact_id' => $membership->contact_id
]));
2500 $this->fixtures
['sched_membership_absolute_date']['entity_value'] = $membership->membership_type_id
;
2501 $this->createScheduleFromFixtures('sched_membership_absolute_date');
2503 $this->assertCronRuns([
2505 // Before the 24-hour mark, no email
2506 'time' => '2012-06-13 04:00:00',
2511 // On absolute date set on 2012-06-14
2512 'time' => '2012-06-14 00:00:00',
2513 'recipients' => [['test-member@example.com']],
2514 'subjects' => ['subject sched_membership_absolute_date'],
2517 // Run cron 4 hours later; first message already sent
2518 'time' => '2012-06-14 04:00:00',
2526 * @param string $fixture
2527 * Key from $this->fixtures
2528 * @param string $status
2530 * @param array $emailParams
2532 * @return \CRM_Member_DAO_Membership
2533 * @throws \API_Exception
2535 protected function createMembershipFromFixture(string $fixture, string $status, $emailParams = []): CRM_Member_DAO_Membership
{
2536 /* @var CRM_Member_DAO_Membership $membership */
2537 $membership = $this->createTestObject(
2538 'CRM_Member_DAO_Membership',
2539 array_merge($this->fixtures
[$fixture], ['status_id' => CRM_Core_PseudoConstant
::getKey('CRM_Member_BAO_Membership', 'status_id', $status)])
2542 Civi\Api4\Email
::create(FALSE)->setValues(array_merge([
2543 'contact_id' => $membership->contact_id
,
2544 'location_type_id' => 1,
2545 ], $emailParams))->execute();
2551 * Create action schedule from defined fixtures.
2553 * @param string $fixture
2554 * @param array $extraParams
2556 * @throws \CRM_Core_Exception
2558 protected function createScheduleFromFixtures(string $fixture, $extraParams = []): void
{
2559 $id = $this->callAPISuccess('ActionSchedule', 'create', array_merge($this->fixtures
[$fixture], $extraParams))['id'];
2560 $this->fixtures
[$fixture]['action_schedule_id'] = (int) $id;
2564 * @param string $activityKey
2565 * @param string $contactKey
2567 * @throws \CRM_Core_Exception
2569 protected function createActivityAndContactFromFixtures(string $activityKey = 'phone_call', string $contactKey = 'contact'): void
{
2570 $activity = $this->createTestObject('CRM_Activity_DAO_Activity', $this->fixtures
[$activityKey]);
2571 $contact = $this->callAPISuccess('contact', 'create', $this->fixtures
[$contactKey]);
2575 $source['contact_id'] = $contact['id'];
2576 $source['activity_id'] = $activity->id
;
2577 $source['record_type_id'] = 2;
2578 $activityContact = $this->createTestObject('CRM_Activity_DAO_ActivityContact', $source);
2579 $activityContact->save();