Merge pull request #20479 from demeritcowboy/userload
[civicrm-core.git] / tests / phpunit / CRM / Core / BAO / ActionScheduleTest.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
7d61e75f 4 | Copyright CiviCRM LLC. All rights reserved. |
6a488035 5 | |
7d61e75f
TO
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 |
6a488035 9 +--------------------------------------------------------------------+
d25dd0ee 10 */
6a488035 11
b00b1f56 12use Civi\Api4\Activity;
991cd69b 13use Civi\Api4\ActivityContact;
56ed359c 14use Civi\Api4\MembershipType;
991cd69b 15
e9479dcf 16/**
56ed359c
EM
17 * Class CRM_Core_BAO_ActionScheduleTest.
18 *
2a21d018 19 * @group ActionSchedule
acb109b7 20 * @group headless
2a21d018
TO
21 *
22 * There are additional tests for some specific entities in other classes:
23 * @see CRM_Activity_ActionMappingTest
24 * @see CRM_Contribute_ActionMapping_ByTypeTest
e9479dcf 25 */
6a488035 26class CRM_Core_BAO_ActionScheduleTest extends CiviUnitTestCase {
63e9c3fd 27
98902dea 28 use CRMTraits_Custom_CustomDataTrait;
29
6a488035 30 /**
f8c3594b 31 * @var CiviMailUtils
6a488035 32 */
567b2076 33 public $mut;
cee19268 34
34662dda 35 /**
36 * Entities set up for the test.
37 *
38 * @var array
39 */
40 private $fixtures = [];
41
56ed359c
EM
42 /**
43 * Generic usable membership type id.
44 *
45 * These should pre-exist but something is deleting them.
46 *
47 * @var int
48 */
49 protected $membershipTypeID;
50
34662dda 51 /**
52 * Setup for tests.
53 *
54 * @throws CRM_Core_Exception
55 */
5bcf6842 56 public function setUp(): void {
6a488035
TO
57 parent::setUp();
58
6c6e6187 59 $this->mut = new CiviMailUtils($this, TRUE);
6a488035 60
9099cab3 61 $this->fixtures['rolling_membership_type'] = [
e08fae02
PH
62 'period_type' => 'rolling',
63 'duration_unit' => 'month',
64 'duration_interval' => '3',
65 'is_active' => 1,
66 'domain_id' => 1,
67 'financial_type_id' => 2,
9099cab3 68 ];
e08fae02 69
9099cab3
CW
70 $this->fixtures['rolling_membership'] = [
71 'membership_type_id' => [
6a488035
TO
72 'period_type' => 'rolling',
73 'duration_unit' => 'month',
74 'duration_interval' => '3',
75 'is_active' => 1,
9099cab3 76 ],
6a488035
TO
77 'join_date' => '20120315',
78 'start_date' => '20120315',
79 'end_date' => '20120615',
b29be8c2 80 'is_override' => 0,
9099cab3 81 ];
4b1efa1d 82
9099cab3
CW
83 $this->fixtures['rolling_membership_past'] = [
84 'membership_type_id' => [
4b1efa1d 85 'period_type' => 'rolling',
86 'duration_unit' => 'month',
87 'duration_interval' => '3',
88 'is_active' => 1,
9099cab3 89 ],
4b1efa1d 90 'join_date' => '20100310',
91 'start_date' => '20100310',
92 'end_date' => '20100610',
93 'is_override' => 'NULL',
9099cab3
CW
94 ];
95 $this->fixtures['participant'] = [
96 'event_id' => [
d3eae6ea
TO
97 'is_active' => 1,
98 'is_template' => 0,
99 'title' => 'Example Event',
100 'start_date' => '20120315',
101 'end_date' => '20120615',
9099cab3 102 ],
39b959db
SL
103 // Attendee.
104 'role_id' => '1',
105 // No-show.
106 'status_id' => '8',
9099cab3 107 ];
fe265b4e 108
5bcf6842 109 $this->fixtures['phone_call'] = [
6a488035
TO
110 'status_id' => 1,
111 'activity_type_id' => 2,
112 'activity_date_time' => '20120615100000',
113 'is_current_revision' => 1,
114 'is_deleted' => 0,
9099cab3
CW
115 ];
116 $this->fixtures['contact'] = [
b29be8c2 117 'is_deceased' => 0,
6a488035
TO
118 'contact_type' => 'Individual',
119 'email' => 'test-member@example.com',
43ceab3f 120 'gender_id' => 'Female',
bba4f3f6
CB
121 'first_name' => 'Churmondleia',
122 'last_name' => 'Ōtākou',
9099cab3
CW
123 ];
124 $this->fixtures['contact_2'] = [
e08fae02
PH
125 'is_deceased' => 0,
126 'contact_type' => 'Individual',
127 'email' => 'test-contact-2@example.com',
128 'gender_id' => 'Male',
c5e1bf98 129 'first_name' => 'Fabio',
e08fae02 130 'last_name' => 'Fi',
9099cab3
CW
131 ];
132 $this->fixtures['contact_birthdate'] = [
43030baf
AH
133 'is_deceased' => 0,
134 'contact_type' => 'Individual',
c5e1bf98 135 'email' => 'test-birth_day@example.com',
43030baf 136 'birth_date' => '20050707',
9099cab3
CW
137 ];
138 $this->fixtures['sched_activity_1day'] = [
6a488035
TO
139 'name' => 'One_Day_Phone_Call_Notice',
140 'title' => 'One Day Phone Call Notice',
84a3e359 141 'limit_to' => '1',
6a488035 142 'absolute_date' => NULL,
d19632b1
JP
143 'body_html' => '<p>1-Day (non-repeating) (for {activity.subject})</p>',
144 'body_text' => '1-Day (non-repeating) (for {activity.subject})',
6a488035
TO
145 'end_action' => NULL,
146 'end_date' => NULL,
147 'end_frequency_interval' => NULL,
148 'end_frequency_unit' => NULL,
149 'entity_status' => '1',
150 'entity_value' => '2',
151 'group_id' => NULL,
152 'is_active' => '1',
153 'is_repeat' => '0',
154 'mapping_id' => '1',
155 'msg_template_id' => NULL,
156 'recipient' => '2',
157 'recipient_listing' => NULL,
158 'recipient_manual' => NULL,
d19632b1 159 'record_activity' => 1,
6a488035
TO
160 'repetition_frequency_interval' => NULL,
161 'repetition_frequency_unit' => NULL,
162 'start_action_condition' => 'before',
163 'start_action_date' => 'activity_date_time',
164 'start_action_offset' => '1',
165 'start_action_unit' => 'day',
f8c3594b 166 'subject' => '1-Day (non-repeating) (about {activity.activity_type})',
9099cab3
CW
167 ];
168 $this->fixtures['sched_activity_1day_r'] = [
6a488035
TO
169 'name' => 'One_Day_Phone_Call_Notice_R',
170 'title' => 'One Day Phone Call Notice R',
84a3e359 171 'limit_to' => 1,
6a488035
TO
172 'absolute_date' => NULL,
173 'body_html' => '<p>1-Day (repeating)</p>',
174 'body_text' => '1-Day (repeating)',
175 'end_action' => 'after',
176 'end_date' => 'activity_date_time',
177 'end_frequency_interval' => '2',
178 'end_frequency_unit' => 'day',
179 'entity_status' => '1',
180 'entity_value' => '2',
181 'group_id' => NULL,
182 'is_active' => '1',
183 'is_repeat' => '1',
184 'mapping_id' => '1',
185 'msg_template_id' => NULL,
186 'recipient' => '2',
187 'recipient_listing' => NULL,
188 'recipient_manual' => NULL,
189 'record_activity' => NULL,
190 'repetition_frequency_interval' => '6',
191 'repetition_frequency_unit' => 'hour',
192 'start_action_condition' => 'before',
193 'start_action_date' => 'activity_date_time',
194 'start_action_offset' => '1',
195 'start_action_unit' => 'day',
f8c3594b 196 'subject' => '1-Day (repeating) (about {activity.activity_type})',
9099cab3
CW
197 ];
198 $this->fixtures['sched_activity_1day_r_on_abs_date'] = [
dc6c330d 199 'name' => 'One_Day_Phone_Call_Notice_R',
200 'title' => 'One Day Phone Call Notice R',
201 'limit_to' => 1,
202 'absolute_date' => CRM_Utils_Date::processDate('20120614100000'),
203 'body_html' => '<p>1-Day (repeating)</p>',
204 'body_text' => '1-Day (repeating)',
205 'entity_status' => '1',
206 'entity_value' => '2',
207 'group_id' => NULL,
208 'is_active' => '1',
209 'is_repeat' => '1',
210 'mapping_id' => '1',
211 'msg_template_id' => NULL,
212 'recipient' => '2',
213 'recipient_listing' => NULL,
214 'recipient_manual' => NULL,
215 'record_activity' => NULL,
216 'repetition_frequency_interval' => '6',
217 'repetition_frequency_unit' => 'hour',
218 'end_action' => 'after',
219 'end_date' => 'activity_date_time',
220 'end_frequency_interval' => '2',
221 'end_frequency_unit' => 'day',
222 'start_action_condition' => '',
223 'start_action_date' => '',
224 'start_action_offset' => '',
225 'start_action_unit' => '',
226 'subject' => '1-Day (repeating) (about {activity.activity_type})',
9099cab3 227 ];
c5e1bf98 228 $this->fixtures['sched_event_name_1day_on_abs_date'] = [
229 'name' => 'sched_event_name_1day_on_abs_date',
230 'title' => 'sched_event_name_1day_on_abs_date',
e08fae02
PH
231 'limit_to' => 1,
232 'absolute_date' => CRM_Utils_Date::processDate('20120614100000'),
c5e1bf98 233 'body_html' => '<p>sched_event_name_1day_on_abs_date</p>',
234 'body_text' => 'sched_event_name_1day_on_abs_date',
e08fae02
PH
235 'entity_status' => '1',
236 'entity_value' => '2',
237 'group_id' => NULL,
238 'is_active' => '1',
239 'is_repeat' => '0',
240 'mapping_id' => '3',
241 'msg_template_id' => NULL,
242 'recipient' => '2',
243 'recipient_listing' => NULL,
244 'recipient_manual' => NULL,
245 'record_activity' => NULL,
246 'repetition_frequency_interval' => NULL,
247 'repetition_frequency_unit' => NULL,
248 'end_action' => NULL,
249 'end_date' => NULL,
250 'end_frequency_interval' => NULL,
251 'end_frequency_unit' => NULL,
252 'start_action_condition' => NULL,
253 'start_action_date' => NULL,
254 'start_action_offset' => NULL,
255 'start_action_unit' => NULL,
c5e1bf98 256 'subject' => 'sched_event_name_1day_on_abs_date',
9099cab3
CW
257 ];
258 $this->fixtures['sched_membership_join_2week'] = [
6a488035
TO
259 'name' => 'sched_membership_join_2week',
260 'title' => 'sched_membership_join_2week',
261 'absolute_date' => '',
262 'body_html' => '<p>body sched_membership_join_2week</p>',
263 'body_text' => 'body sched_membership_join_2week',
264 'end_action' => '',
265 'end_date' => '',
266 'end_frequency_interval' => '',
267 'end_frequency_unit' => '',
268 'entity_status' => '',
269 'entity_value' => '',
270 'group_id' => '',
271 'is_active' => 1,
272 'is_repeat' => '0',
273 'mapping_id' => 4,
274 'msg_template_id' => '',
275 'recipient' => '',
276 'recipient_listing' => '',
277 'recipient_manual' => '',
278 'record_activity' => 1,
279 'repetition_frequency_interval' => '',
280 'repetition_frequency_unit' => '',
281 'start_action_condition' => 'after',
282 'start_action_date' => 'membership_join_date',
283 'start_action_offset' => '2',
284 'start_action_unit' => 'week',
f8c3594b 285 'subject' => 'subject sched_membership_join_2week (joined {membership.join_date})',
9099cab3
CW
286 ];
287 $this->fixtures['sched_membership_start_1week'] = [
bb3ba83e
AH
288 'name' => 'sched_membership_start_1week',
289 'title' => 'sched_membership_start_1week',
290 'absolute_date' => '',
291 'body_html' => '<p>body sched_membership_start_1week</p>',
292 'body_text' => 'body sched_membership_start_1week',
293 'end_action' => '',
294 'end_date' => '',
295 'end_frequency_interval' => '',
296 'end_frequency_unit' => '',
297 'entity_status' => '',
298 'entity_value' => '',
299 'group_id' => '',
300 'is_active' => 1,
301 'is_repeat' => '0',
302 'mapping_id' => 4,
303 'msg_template_id' => '',
304 'recipient' => '',
305 'recipient_listing' => '',
306 'recipient_manual' => '',
307 'record_activity' => 1,
308 'repetition_frequency_interval' => '',
309 'repetition_frequency_unit' => '',
310 'start_action_condition' => 'after',
311 'start_action_date' => 'membership_start_date',
312 'start_action_offset' => '1',
313 'start_action_unit' => 'week',
314 'subject' => 'subject sched_membership_start_1week (joined {membership.start_date})',
9099cab3
CW
315 ];
316 $this->fixtures['sched_membership_end_2week'] = [
6a488035
TO
317 'name' => 'sched_membership_end_2week',
318 'title' => 'sched_membership_end_2week',
319 'absolute_date' => '',
320 'body_html' => '<p>body sched_membership_end_2week</p>',
321 'body_text' => 'body sched_membership_end_2week',
322 'end_action' => '',
323 'end_date' => '',
324 'end_frequency_interval' => '',
325 'end_frequency_unit' => '',
326 'entity_status' => '',
327 'entity_value' => '',
328 'group_id' => '',
329 'is_active' => 1,
330 'is_repeat' => '0',
331 'mapping_id' => 4,
332 'msg_template_id' => '',
333 'recipient' => '',
334 'recipient_listing' => '',
335 'recipient_manual' => '',
336 'record_activity' => 1,
337 'repetition_frequency_interval' => '',
338 'repetition_frequency_unit' => '',
339 'start_action_condition' => 'before',
340 'start_action_date' => 'membership_end_date',
341 'start_action_offset' => '2',
342 'start_action_unit' => 'week',
343 'subject' => 'subject sched_membership_end_2week',
9099cab3
CW
344 ];
345 $this->fixtures['sched_on_membership_end_date'] = [
d2868251 346 'name' => 'sched_on_membership_end_date',
347 'title' => 'sched_on_membership_end_date',
348 'body_html' => '<p>Your membership expired today</p>',
349 'body_text' => 'Your membership expired today',
350 'is_active' => 1,
351 'mapping_id' => 4,
352 'record_activity' => 1,
353 'start_action_condition' => 'after',
354 'start_action_date' => 'membership_end_date',
355 'start_action_offset' => '0',
356 'start_action_unit' => 'hour',
357 'subject' => 'subject send reminder on membership_end_date',
9099cab3
CW
358 ];
359 $this->fixtures['sched_after_1day_membership_end_date'] = [
d2868251 360 'name' => 'sched_after_1day_membership_end_date',
361 'title' => 'sched_after_1day_membership_end_date',
362 'body_html' => '<p>Your membership expired yesterday</p>',
363 'body_text' => 'Your membership expired yesterday',
364 'is_active' => 1,
365 'mapping_id' => 4,
366 'record_activity' => 1,
367 'start_action_condition' => 'after',
368 'start_action_date' => 'membership_end_date',
369 'start_action_offset' => '1',
370 'start_action_unit' => 'day',
371 'subject' => 'subject send reminder on membership_end_date',
9099cab3 372 ];
4b1efa1d 373
9099cab3 374 $this->fixtures['sched_membership_end_2month'] = [
4b1efa1d 375 'name' => 'sched_membership_end_2month',
376 'title' => 'sched_membership_end_2month',
377 'absolute_date' => '',
378 'body_html' => '<p>body sched_membership_end_2month</p>',
379 'body_text' => 'body sched_membership_end_2month',
380 'end_action' => '',
381 'end_date' => '',
382 'end_frequency_interval' => '',
383 'end_frequency_unit' => '',
384 'entity_status' => '',
385 'entity_value' => '',
386 'group_id' => '',
387 'is_active' => 1,
388 'is_repeat' => '0',
389 'mapping_id' => 4,
390 'msg_template_id' => '',
391 'recipient' => '',
392 'recipient_listing' => '',
393 'recipient_manual' => '',
394 'record_activity' => 1,
395 'repetition_frequency_interval' => '',
396 'repetition_frequency_unit' => '',
397 'start_action_condition' => 'after',
398 'start_action_date' => 'membership_end_date',
399 'start_action_offset' => '2',
400 'start_action_unit' => 'month',
401 'subject' => 'subject sched_membership_end_2month',
9099cab3 402 ];
4b1efa1d 403
9099cab3 404 $this->fixtures['sched_membership_absolute_date'] = [
e08fae02
PH
405 'name' => 'sched_membership_absolute_date',
406 'title' => 'sched_membership_absolute_date',
407 'absolute_date' => CRM_Utils_Date::processDate('20120614100000'),
408 'body_html' => '<p>body sched_membership_absolute_date</p>',
409 'body_text' => 'body sched_membership_absolute_date',
410 'end_action' => '',
411 'end_date' => '',
412 'end_frequency_interval' => '',
413 'end_frequency_unit' => '',
414 'entity_status' => '',
415 'entity_value' => '',
416 'group_id' => '',
417 'is_active' => 1,
418 'is_repeat' => '0',
419 'mapping_id' => 4,
420 'msg_template_id' => '',
421 'recipient' => '',
422 'recipient_listing' => '',
423 'recipient_manual' => '',
424 'record_activity' => 1,
425 'repetition_frequency_interval' => '',
426 'repetition_frequency_unit' => '',
427 'start_action_condition' => '',
428 'start_action_date' => '',
429 'start_action_offset' => '',
430 'start_action_unit' => '',
431 'subject' => 'subject sched_membership_absolute_date',
9099cab3 432 ];
e08fae02 433
c5e1bf98 434 $this->fixtures['sched_contact_birth_day_yesterday'] = [
435 'name' => 'sched_contact_birth_day_yesterday',
436 'title' => 'sched_contact_birth_day_yesterday',
43030baf
AH
437 'absolute_date' => '',
438 'body_html' => '<p>you look like you were born yesterday!</p>',
439 'body_text' => 'you look like you were born yesterday!',
440 'end_action' => '',
441 'end_date' => '',
442 'end_frequency_interval' => '',
443 'end_frequency_unit' => '',
444 'entity_status' => 1,
445 'entity_value' => 'birth_date',
446 'group_id' => '',
447 'is_active' => 1,
448 'is_repeat' => '0',
449 'mapping_id' => 6,
450 'msg_template_id' => '',
451 'recipient' => '',
452 'recipient_listing' => '',
453 'recipient_manual' => '',
454 'record_activity' => 1,
455 'repetition_frequency_interval' => '',
456 'repetition_frequency_unit' => '',
457 'start_action_condition' => 'after',
458 'start_action_date' => 'date_field',
459 'start_action_offset' => '1',
460 'start_action_unit' => 'day',
c5e1bf98 461 'subject' => 'subject sched_contact_birth_day_yesterday',
9099cab3 462 ];
43030baf 463
c5e1bf98 464 $this->fixtures['sched_contact_birth_day_anniversary'] = [
465 'name' => 'sched_contact_birth_day_anniversary',
466 'title' => 'sched_contact_birth_day_anniversary',
43030baf
AH
467 'absolute_date' => '',
468 'body_html' => '<p>happy birthday!</p>',
469 'body_text' => 'happy birthday!',
470 'end_action' => '',
471 'end_date' => '',
472 'end_frequency_interval' => '',
473 'end_frequency_unit' => '',
474 'entity_status' => 2,
475 'entity_value' => 'birth_date',
476 'group_id' => '',
477 'is_active' => 1,
478 'is_repeat' => '0',
479 'mapping_id' => 6,
480 'msg_template_id' => '',
481 'recipient' => '',
482 'recipient_listing' => '',
483 'recipient_manual' => '',
484 'record_activity' => 1,
485 'repetition_frequency_interval' => '',
486 'repetition_frequency_unit' => '',
487 'start_action_condition' => 'before',
488 'start_action_date' => 'date_field',
489 'start_action_offset' => '1',
490 'start_action_unit' => 'day',
c5e1bf98 491 'subject' => 'subject sched_contact_birth_day_anniversary',
9099cab3 492 ];
43030baf 493
9099cab3 494 $this->fixtures['sched_contact_grad_tomorrow'] = [
43030baf
AH
495 'name' => 'sched_contact_grad_tomorrow',
496 'title' => 'sched_contact_grad_tomorrow',
497 'absolute_date' => '',
498 'body_html' => '<p>congratulations on your graduation!</p>',
499 'body_text' => 'congratulations on your graduation!',
500 'end_action' => '',
501 'end_date' => '',
502 'end_frequency_interval' => '',
503 'end_frequency_unit' => '',
504 'entity_status' => 1,
505 'group_id' => '',
506 'is_active' => 1,
507 'is_repeat' => '0',
508 'mapping_id' => 6,
509 'msg_template_id' => '',
510 'recipient' => '',
511 'recipient_listing' => '',
512 'recipient_manual' => '',
513 'record_activity' => 1,
514 'repetition_frequency_interval' => '',
515 'repetition_frequency_unit' => '',
516 'start_action_condition' => 'before',
517 'start_action_date' => 'date_field',
518 'start_action_offset' => '1',
519 'start_action_unit' => 'day',
520 'subject' => 'subject sched_contact_grad_tomorrow',
9099cab3 521 ];
43030baf 522
c5e1bf98 523 $this->fixtures['sched_contact_grad_anniversary'] = [
524 'name' => 'sched_contact_grad_anniversary',
525 'title' => 'sched_contact_grad_anniversary',
43030baf
AH
526 'absolute_date' => '',
527 'body_html' => '<p>dear alum, please send us money.</p>',
528 'body_text' => 'dear alum, please send us money.',
529 'end_action' => '',
530 'end_date' => '',
531 'end_frequency_interval' => '',
532 'end_frequency_unit' => '',
533 'entity_status' => 2,
534 'group_id' => '',
535 'is_active' => 1,
536 'is_repeat' => '0',
537 'mapping_id' => 6,
538 'msg_template_id' => '',
539 'recipient' => '',
540 'recipient_listing' => '',
541 'recipient_manual' => '',
542 'record_activity' => 1,
543 'repetition_frequency_interval' => '',
544 'repetition_frequency_unit' => '',
545 'start_action_condition' => 'after',
546 'start_action_date' => 'date_field',
547 'start_action_offset' => '1',
548 'start_action_unit' => 'week',
c5e1bf98 549 'subject' => 'subject sched_contact_grad_anniversary',
9099cab3 550 ];
4aff0253 551
9099cab3 552 $this->fixtures['sched_contact_created_yesterday'] = [
4aff0253
AH
553 'name' => 'sched_contact_created_yesterday',
554 'title' => 'sched_contact_created_yesterday',
555 'absolute_date' => '',
556 'body_html' => '<p>Your contact was created yesterday</p>',
557 'body_text' => 'Your contact was created yesterday!',
558 'end_action' => '',
559 'end_date' => '',
560 'end_frequency_interval' => '',
561 'end_frequency_unit' => '',
562 'entity_status' => 1,
563 'entity_value' => 'created_date',
564 'group_id' => '',
565 'is_active' => 1,
566 'is_repeat' => '0',
567 'mapping_id' => 6,
568 'msg_template_id' => '',
569 'recipient' => '',
570 'recipient_listing' => '',
571 'recipient_manual' => '',
572 'record_activity' => 1,
573 'repetition_frequency_interval' => '',
574 'repetition_frequency_unit' => '',
575 'start_action_condition' => 'after',
576 'start_action_date' => 'date_field',
577 'start_action_offset' => '1',
578 'start_action_unit' => 'day',
579 'subject' => 'subject sched_contact_created_yesterday',
9099cab3 580 ];
4aff0253 581
c5e1bf98 582 $this->fixtures['sched_contact_mod_anniversary'] = [
583 'name' => 'sched_contact_mod_anniversary',
584 'title' => 'sched_contact_mod_anniversary',
4aff0253
AH
585 'absolute_date' => '',
586 'body_html' => '<p>You last updated your data last year</p>',
587 'body_text' => 'Go update your stuff!',
588 'end_action' => '',
589 'end_date' => '',
590 'end_frequency_interval' => '',
591 'end_frequency_unit' => '',
592 'entity_status' => 2,
593 'entity_value' => 'modified_date',
594 'group_id' => '',
595 'is_active' => 1,
596 'is_repeat' => '0',
597 'mapping_id' => 6,
598 'msg_template_id' => '',
599 'recipient' => '',
600 'recipient_listing' => '',
601 'recipient_manual' => '',
602 'record_activity' => 1,
603 'repetition_frequency_interval' => '',
604 'repetition_frequency_unit' => '',
605 'start_action_condition' => 'before',
606 'start_action_date' => 'date_field',
607 'start_action_offset' => '1',
608 'start_action_unit' => 'day',
c5e1bf98 609 'subject' => 'subject sched_contact_mod_anniversary',
9099cab3 610 ];
4aff0253 611
c5e1bf98 612 $this->fixtures['sched_event_type_start_1week_before'] = [
613 'name' => 'sched_event_type_start_1week_before',
614 'title' => 'sched_event_type_start_1week_before',
d3eae6ea 615 'absolute_date' => '',
c5e1bf98 616 'body_html' => '<p>body sched_event_type_start_1week_before ({event.title})</p>',
617 'body_text' => 'body sched_event_type_start_1week_before ({event.title})',
d3eae6ea
TO
618 'end_action' => '',
619 'end_date' => '',
620 'end_frequency_interval' => '',
621 'end_frequency_unit' => '',
39b959db
SL
622 // participant status id
623 'entity_status' => '',
624 // event type id
625 'entity_value' => '',
d3eae6ea
TO
626 'group_id' => '',
627 'is_active' => 1,
628 'is_repeat' => '0',
39b959db
SL
629 // event type
630 'mapping_id' => 2,
d3eae6ea
TO
631 'msg_template_id' => '',
632 'recipient' => '',
633 'recipient_listing' => '',
634 'recipient_manual' => '',
635 'record_activity' => 1,
636 'repetition_frequency_interval' => '',
637 'repetition_frequency_unit' => '',
638 'start_action_condition' => 'before',
639 'start_action_date' => 'event_start_date',
640 'start_action_offset' => '1',
641 'start_action_unit' => 'week',
c5e1bf98 642 'subject' => 'subject sched_event_type_start_1week_before ({event.title})',
9099cab3 643 ];
c5e1bf98 644 $this->fixtures['sched_event_type_end_2month_repeat_twice_2_weeks'] = [
645 'name' => 'sched_event_type_end_2month_repeat_twice_2_weeks',
646 'title' => 'sched_event_type_end_2month_repeat_twice_2_weeks',
d3eae6ea 647 'absolute_date' => '',
c5e1bf98 648 'body_html' => '<p>body sched_event_type_end_2month_repeat_twice_2_weeks {event.title}</p>',
649 'body_text' => 'body sched_event_type_end_2month_repeat_twice_2_weeks {event.title}',
d3eae6ea
TO
650 'end_action' => 'after',
651 'end_date' => 'event_end_date',
652 'end_frequency_interval' => '3',
653 'end_frequency_unit' => 'month',
39b959db
SL
654 // participant status id
655 'entity_status' => '',
656 // event type id
657 'entity_value' => '',
d3eae6ea
TO
658 'group_id' => '',
659 'is_active' => 1,
660 'is_repeat' => '1',
39b959db
SL
661 // event type
662 'mapping_id' => 2,
d3eae6ea
TO
663 'msg_template_id' => '',
664 'recipient' => '',
665 'recipient_listing' => '',
666 'recipient_manual' => '',
667 'record_activity' => 1,
668 'repetition_frequency_interval' => '2',
669 'repetition_frequency_unit' => 'week',
670 'start_action_condition' => 'after',
671 'start_action_date' => 'event_end_date',
672 'start_action_offset' => '2',
673 'start_action_unit' => 'month',
c5e1bf98 674 'subject' => 'subject sched_event_type_end_2month_repeat_twice_2_weeks {event.title}',
9099cab3 675 ];
d3eae6ea 676
9099cab3 677 $this->fixtures['sched_membership_end_2month_repeat_twice_4_weeks'] = [
861d11c4
DG
678 'name' => 'sched_membership_end_2month',
679 'title' => 'sched_membership_end_2month',
680 'absolute_date' => '',
681 'body_html' => '<p>body sched_membership_end_2month</p>',
682 'body_text' => 'body sched_membership_end_2month',
683 'end_action' => '',
5c4d6559 684 'end_date' => 'membership_end_date',
861d11c4
DG
685 'end_frequency_interval' => '4',
686 'end_frequency_unit' => 'month',
687 'entity_status' => '',
688 'entity_value' => '',
689 'group_id' => '',
690 'is_active' => 1,
691 'is_repeat' => '1',
692 'mapping_id' => 4,
693 'msg_template_id' => '',
694 'recipient' => '',
695 'recipient_listing' => '',
696 'recipient_manual' => '',
697 'record_activity' => 1,
698 'repetition_frequency_interval' => '4',
699 'repetition_frequency_unit' => 'week',
700 'start_action_condition' => 'after',
701 'start_action_date' => 'membership_end_date',
702 'start_action_offset' => '2',
703 'start_action_unit' => 'month',
704 'subject' => 'subject sched_membership_end_2month',
9099cab3
CW
705 ];
706 $this->fixtures['sched_membership_end_limit_to_none'] = [
baa85770
EM
707 'name' => 'limit to none',
708 'title' => 'limit to none',
709 'absolute_date' => '',
710 'body_html' => '<p>body sched_membership_end_2month</p>',
711 'body_text' => 'body sched_membership_end_2month',
712 'end_action' => '',
713 'end_date' => '',
714 'end_frequency_interval' => '4',
715 'end_frequency_unit' => 'month',
716 'entity_status' => '',
717 'entity_value' => '',
718 'limit_to' => 0,
719 'group_id' => '',
720 'is_active' => 1,
721 'is_repeat' => '1',
722 'mapping_id' => 4,
723 'msg_template_id' => '',
724 'recipient' => '',
725 'recipient_listing' => '',
726 'recipient_manual' => '',
727 'record_activity' => 1,
728 'repetition_frequency_interval' => '4',
729 'repetition_frequency_unit' => 'week',
730 'start_action_condition' => 'after',
731 'start_action_date' => 'membership_end_date',
732 'start_action_offset' => '2',
733 'start_action_unit' => 'month',
734 'subject' => 'limit to none',
9099cab3
CW
735 ];
736 $this->fixtures['sched_on_membership_end_date_repeat_interval'] = [
52f09320
PH
737 'name' => 'sched_on_membership_end_date',
738 'title' => 'sched_on_membership_end_date',
739 'body_html' => '<p>Your membership expired 1 unit ago</p>',
740 'body_text' => 'Your membership expired 1 unit ago',
741 'end_frequency_interval' => 10,
742 'end_frequency_unit' => 'year',
743 'is_active' => 1,
744 'is_repeat' => TRUE,
745 'mapping_id' => 4,
746 'record_activity' => 1,
747 'start_action_condition' => 'after',
748 'start_action_date' => 'membership_end_date',
749 'start_action_offset' => '0',
750 'start_action_unit' => 'hour',
751 'subject' => 'subject send reminder every unit after membership_end_date',
9099cab3 752 ];
52f09320 753
9099cab3 754 $customGroup = $this->callAPISuccess('CustomGroup', 'create', [
2273ddcb 755 'title' => ts('Test Contact Custom group'),
756 'name' => 'test_contact_cg',
757 'extends' => 'Contact',
758 'domain_id' => CRM_Core_Config::domainID(),
759 'is_active' => 1,
760 'collapse_adv_display' => 0,
761 'collapse_display' => 0,
9099cab3
CW
762 ]);
763 $customField = $this->callAPISuccess('CustomField', 'create', [
2273ddcb 764 'label' => 'Test Text',
765 'data_type' => 'String',
766 'html_type' => 'Text',
767 'custom_group_id' => $customGroup['id'],
9099cab3 768 ]);
d1e4008e
MD
769 $customDateField = $this->callAPISuccess('CustomField', 'create', [
770 'label' => 'Test Date Field',
771 'data_type' => 'Date',
772 'html_type' => 'Select Date',
773 'date_format' => 'mm/dd/yy',
774 'custom_group_id' => $customGroup['id'],
775 ]);
776
9099cab3 777 $this->fixtures['contact_custom_token'] = [
2273ddcb 778 'id' => $customField['id'],
779 'token' => sprintf('{contact.custom_%s}', $customField['id']),
780 'name' => sprintf('custom_%s', $customField['id']),
c5e1bf98 781 'value' => 'text ' . substr(sha1(mt_rand()), 0, 7),
9099cab3 782 ];
2273ddcb 783
d1e4008e
MD
784 $this->fixtures['sched_on_custom_date'] = [
785 'name' => 'sched_on_custom_date',
786 'title' => 'sched_on_custom_date',
787 'body_html' => '<p>Send reminder before 1 hour of custom date field</p>',
788 'body_text' => 'Send reminder on custom date field',
789 'subject' => 'Send reminder on custom date field',
790 'mapping_id' => 6,
791 'entity_value' => 'custom_' . $customDateField['id'],
792 'entity_status' => 2,
793 'entity' => [
794 6,
795 ['custom_' . $customDateField['id']],
796 [1],
797 ],
798 'start_action_offset' => 1,
799 'start_action_unit' => 'hour',
800 'start_action_condition' => 'before',
801 'start_action_date' => 'date_field',
802 'record_activity' => 1,
803 'repetition_frequency_unit' => 'hour',
804 'end_frequency_unit' => 'hour',
805 'end_action' => 'before',
806 'end_date' => 'date_field',
807 'custom_field_name' => 'custom_' . $customDateField['id'],
808 ];
6a488035
TO
809 }
810
811 /**
812 * Tears down the fixture, for example, closes a network connection.
92c99a4a 813 *
6a488035 814 * This method is called after a test is executed.
34662dda 815 *
816 * @throws \CRM_Core_Exception
edcbcbb0 817 * @throws \CiviCRM_API3_Exception
818 * @throws \API_Exception
6a488035 819 */
5bcf6842 820 public function tearDown(): void {
56ed359c
EM
821 $this->deleteTestObjects();
822 MembershipType::delete()->addWhere('name', 'NOT IN', ['General', 'Student', 'Lifetime'])->execute();
9099cab3 823 $this->quickCleanup([
92c99a4a
EM
824 'civicrm_action_schedule',
825 'civicrm_action_log',
826 'civicrm_membership',
c5e1bf98 827 'civicrm_line_item',
d3eae6ea
TO
828 'civicrm_participant',
829 'civicrm_event',
92c99a4a 830 'civicrm_email',
98902dea 831 ], TRUE);
56ed359c 832 $this->quickCleanUpFinancialEntities();
edcbcbb0 833 parent::tearDown();
6a488035
TO
834 }
835
56ed359c
EM
836 /**
837 * Get a usable membership type id - creating one if none exists.
838 *
839 * It should exist but this class over-deletes in not-fully-diagnosed places.
840 *
841 * @throws \API_Exception
842 */
843 protected function getMembershipTypeID(): int {
844 $generalTypeID = CRM_Core_PseudoConstant::getKey('CRM_Member_BAO_Membership', 'membership_type_id', 'General');
845 if ($generalTypeID) {
846 $this->membershipTypeID = $generalTypeID;
847 }
848 else {
849 $this->membershipTypeID = (int) MembershipType::create()
850 ->setValues([
851 'name' => 'General',
852 'period_type' => 'rolling',
853 'member_of_contact_id' => 1,
854 'financial_type_id:name' => 'Member Dues',
855 'duration_unit' => 1,
856 ]
857 )->execute()->first()['id'];
858 }
859 return $this->membershipTypeID;
860 }
861
34662dda 862 /**
863 * Get mailer examples.
864 *
865 * @return array
866 */
5bcf6842 867 public function mailerExamples(): array {
9099cab3 868 $cases = [];
43ceab3f 869
bba4f3f6 870 // Some tokens - short as subject has 128char limit in DB.
9099cab3 871 $someTokensTmpl = implode(';;', [
39b959db
SL
872 // basic contact token
873 '{contact.display_name}',
874 // funny legacy contact token
875 '{contact.gender}',
876 // funny legacy contact token
877 '{contact.gender_id}',
878 // domain token
879 '{domain.name}',
880 // action-scheduler token
881 '{activity.activity_type}',
9099cab3 882 ]);
bba4f3f6 883 // Further tokens can be tested in the body text/html.
9099cab3 884 $manyTokensTmpl = implode(';;', [
bba4f3f6
CB
885 $someTokensTmpl,
886 '{contact.email_greeting}',
34662dda 887 $this->fixtures['contact_custom_token']['token'],
9099cab3 888 ]);
ccdf6bd5
TO
889 // Note: The behavior of domain-tokens on a scheduled reminder is undefined. All we
890 // can really do is check that it has something.
bba4f3f6 891 $someTokensExpected = 'Churmondleia Ōtākou;;Female;;Female;;[a-zA-Z0-9 ]+;;Phone Call';
34662dda 892 $manyTokensExpected = sprintf('%s;;Dear Churmondleia;;%s', $someTokensExpected, $this->fixtures['contact_custom_token']['value']);
43ceab3f 893
bba4f3f6 894 // In this example, we use a lot of tokens cutting across multiple components.
9099cab3 895 $cases[0] = [
43ceab3f 896 // Schedule definition.
9099cab3 897 [
bba4f3f6 898 'subject' => "subj $someTokensTmpl",
43ceab3f
TO
899 'body_html' => "html $manyTokensTmpl",
900 'body_text' => "text $manyTokensTmpl",
9099cab3 901 ],
43ceab3f 902 // Assertions (regex).
9099cab3 903 [
34662dda 904 'from_name' => '/^FIXME$/',
905 'from_email' => '/^info@EXAMPLE.ORG$/',
bba4f3f6 906 'subject' => "/^subj $someTokensExpected\$/",
43ceab3f
TO
907 'body_html' => "/^html $manyTokensExpected\$/",
908 'body_text' => "/^text $manyTokensExpected\$/",
9099cab3
CW
909 ],
910 ];
43ceab3f
TO
911
912 // In this example, we customize the from address.
9099cab3 913 $cases[1] = [
43ceab3f 914 // Schedule definition.
9099cab3 915 [
43ceab3f
TO
916 'from_name' => 'Bob',
917 'from_email' => 'bob@example.org',
9099cab3 918 ],
43ceab3f 919 // Assertions (regex).
9099cab3 920 [
34662dda 921 'from_name' => '/^Bob$/',
922 'from_email' => '/^bob@example.org$/',
9099cab3
CW
923 ],
924 ];
43ceab3f 925
c5e1bf98 926 // In this example, we auto-convert HTML to text
9099cab3 927 $cases[2] = [
43ceab3f 928 // Schedule definition.
9099cab3 929 [
43ceab3f
TO
930 'body_html' => '<p>Hello &amp; stuff.</p>',
931 'body_text' => '',
9099cab3 932 ],
43ceab3f 933 // Assertions (regex).
9099cab3 934 [
43ceab3f
TO
935 'body_html' => '/^' . preg_quote('<p>Hello &amp; stuff.</p>', '/') . '/',
936 'body_text' => '/^' . preg_quote('Hello & stuff.', '/') . '/',
9099cab3
CW
937 ],
938 ];
43ceab3f
TO
939
940 // In this example, we autoconvert HTML to text
9099cab3 941 $cases[3] = [
43ceab3f 942 // Schedule definition.
9099cab3 943 [
43ceab3f
TO
944 'body_html' => '',
945 'body_text' => 'Hello world',
9099cab3 946 ],
43ceab3f 947 // Assertions (regex).
9099cab3 948 [
43ceab3f
TO
949 'body_html' => '/^--UNDEFINED--$/',
950 'body_text' => '/^Hello world$/',
9099cab3
CW
951 ],
952 ];
43ceab3f
TO
953
954 return $cases;
955 }
956
957 /**
958 * This generates a single mailing through the scheduled-reminder
959 * system (using an activity-reminder as a baseline) and
960 * checks that the resulting message satisfies various
961 * regular expressions.
962 *
963 * @param array $schedule
964 * Values to set/override in the schedule.
965 * Ex: array('subject' => 'Hello, {contact.first_name}!').
966 * @param array $patterns
967 * A list of regexes to compare with the actual email.
968 * Ex: array('subject' => '/^Hello, Alice!/').
969 * Keys: subject, body_text, body_html, from_name, from_email.
34662dda 970 *
991cd69b 971 * @throws \API_Exception
972 * @throws \CRM_Core_Exception
973 * @throws \Civi\API\Exception\UnauthorizedException
43ceab3f
TO
974 * @dataProvider mailerExamples
975 */
c5e1bf98 976 public function testMailer(array $schedule, array $patterns): void {
977 $this->createScheduleFromFixtures('sched_activity_1day', $schedule);
5bcf6842 978 $activity = $this->createTestObject('CRM_Activity_DAO_Activity', $this->fixtures['phone_call']);
2273ddcb 979 $contact = $this->callAPISuccess('contact', 'create', array_merge(
980 $this->fixtures['contact'],
9099cab3 981 [
2273ddcb 982 $this->fixtures['contact_custom_token']['name'] => $this->fixtures['contact_custom_token']['value'],
9099cab3 983 ]
2273ddcb 984 ));
43ceab3f
TO
985 $activity->save();
986
fe806431 987 ActivityContact::create(FALSE)->setValues([
991cd69b 988 'contact_id' => $contact['id'],
989 'activity_id' => $activity->id,
990 'record_type_id:name' => 'Activity Source',
991 ])->execute();
43ceab3f
TO
992
993 CRM_Utils_Time::setTime('2012-06-14 15:00:00');
34662dda 994 $this->callAPISuccess('job', 'send_reminder');
9099cab3 995 $this->mut->assertRecipients([['test-member@example.com']]);
43ceab3f
TO
996 foreach ($this->mut->getAllMessages('ezc') as $message) {
997 /** @var ezcMail $message */
998
9099cab3 999 $messageArray = [];
43ceab3f
TO
1000 $messageArray['subject'] = $message->subject;
1001 $messageArray['from_name'] = $message->from->name;
1002 $messageArray['from_email'] = $message->from->email;
1003 $messageArray['body_text'] = '--UNDEFINED--';
1004 $messageArray['body_html'] = '--UNDEFINED--';
1005
1006 foreach ($message->fetchParts() as $part) {
1007 /** @var ezcMailText ezcMailText */
34662dda 1008 if ($part instanceof ezcMailText && $part->subType === 'html') {
43ceab3f
TO
1009 $messageArray['body_html'] = $part->text;
1010 }
34662dda 1011 if ($part instanceof ezcMailText && $part->subType === 'plain') {
43ceab3f
TO
1012 $messageArray['body_text'] = $part->text;
1013 }
1014 }
1015
1016 foreach ($patterns as $field => $pattern) {
1017 $this->assertRegExp($pattern, $messageArray[$field],
9099cab3 1018 "Check that '$field'' matches regex. " . print_r(['expected' => $patterns, 'actual' => $messageArray], 1));
43ceab3f
TO
1019 }
1020 }
1021 $this->mut->clearMessages();
43ceab3f
TO
1022 }
1023
d1e4008e
MD
1024 /**
1025 * Send reminder 1 hour before custom date field
56ed359c
EM
1026 *
1027 * @throws \CRM_Core_Exception
d1e4008e
MD
1028 */
1029 public function testReminderWithCustomDateField(): void {
1030 $this->createScheduleFromFixtures('sched_on_custom_date');
1031 $this->callAPISuccess('contact', 'create', array_merge($this->fixtures['contact'], [$this->fixtures['sched_on_custom_date']['custom_field_name'] => '04/06/2021']));
1032 $this->assertCronRuns([
1033 [
1034 // Before the 24-hour mark, no email
1035 'time' => '2021-04-02 04:00:00',
1036 'recipients' => [],
1037 'subjects' => [],
1038 ],
1039 [
1040 // After the 24-hour mark, an email
1041 'time' => '2021-04-05 23:00:00',
1042 'recipients' => [['test-member@example.com']],
1043 'subjects' => ['Send reminder on custom date field'],
1044 ],
1045 [
1046 // Run cron again; message already sent
1047 'time' => '',
1048 'recipients' => [],
1049 ],
1050 ]);
1051 }
1052
34662dda 1053 /**
1054 * Test calculated activity schedule.
1055 *
b00b1f56 1056 * @throws \API_Exception
34662dda 1057 * @throws \CRM_Core_Exception
1058 */
5bcf6842 1059 public function testActivityDateTimeMatchNonRepeatableSchedule(): void {
34662dda 1060 $this->createScheduleFromFixtures('sched_activity_1day');
6a488035 1061
5bcf6842 1062 $activity = $this->createTestObject('CRM_Activity_DAO_Activity', $this->fixtures['phone_call']);
6c6e6187 1063 $contact = $this->callAPISuccess('contact', 'create', $this->fixtures['contact']);
5bcf6842 1064 $activity->subject = 'Test subject for phone_call';
6a488035
TO
1065 $activity->save();
1066
4f20f356 1067 $source['contact_id'] = $contact['id'];
1068 $source['activity_id'] = $activity->id;
1069 $source['record_type_id'] = 2;
1070 $activityContact = $this->createTestObject('CRM_Activity_DAO_ActivityContact', $source);
1071 $activityContact->save();
1072
9099cab3
CW
1073 $this->assertCronRuns([
1074 [
567b2076 1075 // Before the 24-hour mark, no email
6a488035 1076 'time' => '2012-06-14 04:00:00',
9099cab3
CW
1077 'recipients' => [],
1078 'subjects' => [],
1079 ],
1080 [
567b2076 1081 // After the 24-hour mark, an email
6a488035 1082 'time' => '2012-06-14 15:00:00',
9099cab3
CW
1083 'recipients' => [['test-member@example.com']],
1084 'subjects' => ['1-Day (non-repeating) (about Phone Call)'],
1085 ],
1086 [
567b2076 1087 // Run cron again; message already sent
6a488035 1088 'time' => '',
9099cab3
CW
1089 'recipients' => [],
1090 ],
1091 ]);
b00b1f56 1092 $activities = Activity::get(FALSE)
1093 ->setSelect(['details'])
1094 ->addWhere('activity_type_id:name', '=', 'Reminder Sent')
1095 ->addWhere('source_record_id', '=', $activity->id)
1096 ->execute();
1097 foreach ($activities as $activityDetails) {
275686a3 1098 $this->assertStringContainsString($activity->subject, $activityDetails['details']);
d19632b1 1099 }
6a488035
TO
1100 }
1101
34662dda 1102 /**
1103 * Test schedule creation on repeatable schedule.
1104 *
1105 * @throws \CRM_Core_Exception
1106 */
5bcf6842 1107 public function testActivityDateTimeMatchRepeatableSchedule(): void {
34662dda 1108 $this->createScheduleFromFixtures('sched_activity_1day_r');
e8113103 1109 $this->createActivityAndContactFromFixtures();
4f20f356 1110
9099cab3
CW
1111 $this->assertCronRuns([
1112 [
567b2076 1113 // Before the 24-hour mark, no email
5bcf6842 1114 'time' => '2012-06-14 04:00:00',
9099cab3
CW
1115 'recipients' => [],
1116 'subjects' => [],
1117 ],
1118 [
567b2076 1119 // After the 24-hour mark, an email
6a488035 1120 'time' => '2012-06-14 15:00:00',
9099cab3
CW
1121 'recipients' => [['test-member@example.com']],
1122 'subjects' => ['1-Day (repeating) (about Phone Call)'],
1123 ],
1124 [
567b2076 1125 // Run cron 4 hours later; first message already sent
6a488035 1126 'time' => '2012-06-14 20:00:00',
9099cab3
CW
1127 'recipients' => [],
1128 'subjects' => [],
1129 ],
1130 [
567b2076 1131 // Run cron 6 hours later; send second message.
6a488035 1132 'time' => '2012-06-14 21:00:01',
9099cab3
CW
1133 'recipients' => [['test-member@example.com']],
1134 'subjects' => ['1-Day (repeating) (about Phone Call)'],
1135 ],
1136 ]);
6a488035
TO
1137 }
1138
34662dda 1139 /**
1140 * @throws \CRM_Core_Exception
1141 */
5bcf6842 1142 public function testActivityDateTimeMatchRepeatableScheduleOnAbsDate(): void {
34662dda 1143 $this->createScheduleFromFixtures('sched_activity_1day_r_on_abs_date');
e8113103 1144 $this->createActivityAndContactFromFixtures();
dc6c330d 1145
9099cab3
CW
1146 $this->assertCronRuns([
1147 [
dc6c330d 1148 // Before the 24-hour mark, no email
1149 'time' => '2012-06-13 04:00:00',
9099cab3
CW
1150 'recipients' => [],
1151 'subjects' => [],
1152 ],
1153 [
dc6c330d 1154 // On absolute date set on 2012-06-14
1155 'time' => '2012-06-14 00:00:00',
9099cab3
CW
1156 'recipients' => [['test-member@example.com']],
1157 'subjects' => ['1-Day (repeating) (about Phone Call)'],
1158 ],
1159 [
dc6c330d 1160 // Run cron 4 hours later; first message already sent
1161 'time' => '2012-06-14 04:00:00',
9099cab3
CW
1162 'recipients' => [],
1163 'subjects' => [],
1164 ],
1165 [
dc6c330d 1166 // Run cron 6 hours later; send second message.
1167 'time' => '2012-06-14 06:00:01',
9099cab3
CW
1168 'recipients' => [['test-member@example.com']],
1169 'subjects' => ['1-Day (repeating) (about Phone Call)'],
1170 ],
1171 ]);
dc6c330d 1172 }
1173
34662dda 1174 /**
1175 * Test event with only an absolute date.
1176 *
1177 * @throws \CRM_Core_Exception
1178 */
5bcf6842 1179 public function testEventNameWithAbsoluteDateAndNothingElse(): void {
9099cab3
CW
1180 $participant = $this->createTestObject('CRM_Event_DAO_Participant', array_merge($this->fixtures['participant'], ['status_id' => 1]));
1181 $this->callAPISuccess('Email', 'create', [
e08fae02
PH
1182 'contact_id' => $participant->contact_id,
1183 'email' => 'test-event@example.com',
9099cab3
CW
1184 ]);
1185 $this->callAPISuccess('contact', 'create', array_merge($this->fixtures['contact'], ['contact_id' => $participant->contact_id]));
e08fae02 1186
c5e1bf98 1187 $actionSchedule = $this->fixtures['sched_event_name_1day_on_abs_date'];
e08fae02
PH
1188 $actionSchedule['entity_value'] = $participant->event_id;
1189 $this->callAPISuccess('action_schedule', 'create', $actionSchedule);
1190
9099cab3
CW
1191 $this->assertCronRuns([
1192 [
e08fae02
PH
1193 // Before the 24-hour mark, no email
1194 'time' => '2012-06-13 04:00:00',
9099cab3
CW
1195 'recipients' => [],
1196 'subjects' => [],
1197 ],
1198 [
e08fae02
PH
1199 // On absolute date set on 2012-06-14
1200 'time' => '2012-06-14 00:00:00',
9099cab3 1201 'recipients' => [['test-event@example.com']],
c5e1bf98 1202 'subjects' => ['sched_event_name_1day_on_abs_date'],
9099cab3
CW
1203 ],
1204 [
e08fae02
PH
1205 // Run cron 4 hours later; first message already sent
1206 'time' => '2012-06-14 04:00:00',
9099cab3
CW
1207 'recipients' => [],
1208 'subjects' => [],
1209 ],
1210 ]);
e08fae02
PH
1211 }
1212
6a488035 1213 /**
76ea51c7 1214 * For contacts/members which match schedule based on join/start date,
6a488035 1215 * an email should be sent.
34662dda 1216 *
56ed359c 1217 * @throws \API_Exception
34662dda 1218 * @throws \CRM_Core_Exception
56ed359c 1219 * @throws \CiviCRM_API3_Exception
6a488035 1220 */
5bcf6842 1221 public function testMembershipDateMatch(): void {
56ed359c
EM
1222 $contactID = $this->individualCreate(array_merge($this->fixtures['contact'], ['email' => 'test-member@example.com']));
1223 $membershipTypeID = $this->getMembershipTypeID();
1224 $membership = (array) $this->callAPISuccess('Membership', 'create', array_merge($this->fixtures['rolling_membership'], ['status_id' => 1, 'contact_id' => $contactID, 'sequential' => 1, 'membership_type_id' => $membershipTypeID]))['values'][0];
1225 $this->createScheduleFromFixtures('sched_membership_join_2week', ['entity_value' => $membershipTypeID]);
6a488035 1226
bb3ba83e 1227 // start_date=2012-03-15 ; schedule is 2 weeks after join_date
9099cab3
CW
1228 $this->assertCronRuns([
1229 [
bb3ba83e
AH
1230 // Before the 2-week mark, no email.
1231 'time' => '2012-03-28 01:00:00',
9099cab3
CW
1232 'recipients' => [],
1233 'subjects' => [],
1234 ],
1235 [
bb3ba83e
AH
1236 // After the 2-week mark, send an email.
1237 'time' => '2012-03-29 01:00:00',
9099cab3
CW
1238 'recipients' => [['test-member@example.com']],
1239 'subjects' => ['subject sched_membership_join_2week (joined March 15th, 2012)'],
1240 ],
1241 ]);
bb3ba83e 1242
56ed359c 1243 $this->createScheduleFromFixtures('sched_membership_start_1week', ['entity_value' => $membership['membership_type_id']]);
bb3ba83e
AH
1244
1245 // start_date=2012-03-15 ; schedule is 1 weeks after start_date
9099cab3
CW
1246 $this->assertCronRuns([
1247 [
bb3ba83e
AH
1248 // Before the 2-week mark, no email.
1249 'time' => '2012-03-21 01:00:00',
9099cab3
CW
1250 'recipients' => [],
1251 'subjects' => [],
1252 ],
1253 [
bb3ba83e
AH
1254 // After the 2-week mark, send an email.
1255 'time' => '2012-03-22 01:00:00',
9099cab3
CW
1256 'recipients' => [['test-member@example.com']],
1257 'subjects' => ['subject sched_membership_start_1week (joined March 15th, 2012)'],
1258 ],
1259 ]);
6a488035
TO
1260 }
1261
cc949606 1262 /**
1263 * CRM-21675: Support parent and smart group in 'Limit to' field
34662dda 1264 *
1265 * @throws \CRM_Core_Exception
5bcf6842 1266 * @throws \CiviCRM_API3_Exception
cc949606 1267 */
5bcf6842 1268 public function testScheduleReminderWithParentGroup(): void {
cc949606 1269 // Contact A with birth-date at '07-07-2005' and gender - Male, later got added in smart group
34662dda 1270 $this->individualCreate(['birth_date' => '20050707', 'gender_id' => 1, 'email' => 'abc@test.com']);
cc949606 1271 // Contact B with birth-date at '07-07-2005', later got added in regular group
9099cab3 1272 $contactID2 = $this->individualCreate(['birth_date' => '20050707', 'email' => 'def@test.com'], 1);
cc949606 1273 // Contact C with birth-date at '07-07-2005', but not included in any group
34662dda 1274 $this->individualCreate(['birth_date' => '20050707', 'email' => 'ghi@test.com'], 2);
cc949606 1275
1276 // create regular group and add Contact B to it
1277 $groupID = $this->groupCreate();
9099cab3 1278 $this->callAPISuccess('GroupContact', 'Create', [
cc949606 1279 'group_id' => $groupID,
1280 'contact_id' => $contactID2,
9099cab3 1281 ]);
cc949606 1282
1283 // create smart group which will contain all Male contacts
fe38e286 1284 $smartGroupParams = ['form_values' => ['gender_id' => 1]];
cc949606 1285 $smartGroupID = $this->smartGroupCreate(
1286 $smartGroupParams,
9099cab3 1287 [
cc949606 1288 'name' => 'new_smart_group',
1289 'title' => 'New Smart Group',
9099cab3
CW
1290 'parents' => [$groupID => 1],
1291 ]
cc949606 1292 );
1293
9099cab3 1294 $actionScheduleParams = [
c5e1bf98 1295 'name' => 'sched_contact_birth_day_yesterday',
1296 'title' => 'sched_contact_birth_day_yesterday',
cc949606 1297 'absolute_date' => '',
1298 'body_html' => '<p>you look like you were born yesterday!</p>',
1299 'body_text' => 'you look like you were born yesterday!',
1300 'end_action' => '',
1301 'end_date' => '',
1302 'end_frequency_interval' => '',
1303 'end_frequency_unit' => '',
1304 'entity_status' => 1,
1305 'entity_value' => 'birth_date',
1306 'limit_to' => 1,
1307 'group_id' => $groupID,
1308 'is_active' => 1,
1309 'is_repeat' => '0',
1310 'mapping_id' => 6,
1311 'msg_template_id' => '',
1312 'recipient' => '2',
1313 'recipient_listing' => '',
1314 'recipient_manual' => '',
1315 'record_activity' => 1,
1316 'repetition_frequency_interval' => '',
1317 'repetition_frequency_unit' => '',
1318 'start_action_condition' => 'after',
1319 'start_action_date' => 'date_field',
1320 'start_action_offset' => '1',
1321 'start_action_unit' => 'day',
c5e1bf98 1322 'subject' => 'subject sched_contact_birth_day_yesterday',
9099cab3 1323 ];
cc949606 1324
c5e1bf98 1325 // Create schedule reminder where parent group ($groupID) is selected to limit recipients,
cc949606 1326 // which contain a individual contact - $contactID2 and is parent to smart group.
c5e1bf98 1327 $this->callAPISuccess('ActionSchedule', 'create', $actionScheduleParams);
9099cab3
CW
1328 $this->assertCronRuns([
1329 [
cc949606 1330 // On the birthday, no email.
1331 'time' => '2005-07-07 01:00:00',
9099cab3
CW
1332 'recipients' => [],
1333 ],
1334 [
cc949606 1335 // The next day, send an email.
1336 'time' => '2005-07-08 20:00:00',
9099cab3
CW
1337 'recipients' => [
1338 [
cc949606 1339 'def@test.com',
9099cab3
CW
1340 ],
1341 [
cc949606 1342 'abc@test.com',
9099cab3
CW
1343 ],
1344 ],
1345 ],
1346 ]);
cc949606 1347 $this->groupDelete($smartGroupID);
1348 $this->groupDelete($groupID);
1349 }
1350
6a488035 1351 /**
442cf836
EM
1352 * Test end date email sent.
1353 *
6a488035
TO
1354 * For contacts/members which match schedule based on join date,
1355 * an email should be sent.
34662dda 1356 *
1357 * @throws \API_Exception
5bcf6842 1358 * @throws \CRM_Core_Exception
56ed359c 1359 * @throws \CiviCRM_API3_Exception
6a488035 1360 */
5bcf6842 1361 public function testMembershipJoinDateNonMatch(): void {
34662dda 1362 $this->createMembershipFromFixture('rolling_membership', '', ['email' => 'test-member@example.com']);
6a488035 1363 // Add an alternative membership type, and only send messages for that type
9099cab3 1364 $extraMembershipType = $this->createTestObject('CRM_Member_DAO_MembershipType', []);
c5e1bf98 1365 $this->createScheduleFromFixtures('sched_membership_join_2week', ['entity_value' => $extraMembershipType->id]);
6a488035
TO
1366
1367 // start_date=2012-03-15 ; schedule is 2 weeks after start_date
9099cab3
CW
1368 $this->assertCronRuns([
1369 [
567b2076 1370 // After the 2-week mark, don't send email because we have different membership type.
6a488035 1371 'time' => '2012-03-29 01:00:00',
9099cab3
CW
1372 'recipients' => [],
1373 ],
1374 ]);
6a488035
TO
1375 }
1376
861d11c4 1377 /**
442cf836 1378 * Test that the first and SECOND notifications are sent out.
34662dda 1379 *
5bcf6842 1380 * @throws \API_Exception
34662dda 1381 * @throws \CRM_Core_Exception
56ed359c 1382 * @throws \CiviCRM_API3_Exception
861d11c4 1383 */
5bcf6842 1384 public function testMembershipEndDateRepeat(): void {
861d11c4 1385 // creates membership with end_date = 20120615
34662dda 1386 $membership = $this->createMembershipFromFixture('rolling_membership', 'Current');
1387 $this->callAPISuccess('Email', 'create', [
56ed359c 1388 'contact_id' => $membership['contact_id'],
861d11c4 1389 'email' => 'test-member@example.com',
9099cab3 1390 ]);
56ed359c 1391 $this->callAPISuccess('contact', 'create', array_merge($this->fixtures['contact'], ['contact_id' => $membership['contact_id']]));
861d11c4 1392
56ed359c 1393 $this->createScheduleFromFixtures('sched_membership_end_2month_repeat_twice_4_weeks', ['entity_value' => $membership['membership_type_id']]);
861d11c4
DG
1394
1395 // end_date=2012-06-15 ; schedule is 2 weeks before end_date
9099cab3
CW
1396 $this->assertCronRuns([
1397 [
e08fae02
PH
1398 // After the 1-month mark, no email
1399 'time' => '2012-07-15 01:00:00',
9099cab3
CW
1400 'recipients' => [],
1401 ],
1402 [
e08fae02 1403 // After the 2-month mark, send an email.
861d11c4 1404 'time' => '2012-08-15 01:00:00',
9099cab3
CW
1405 'recipients' => [['test-member@example.com']],
1406 ],
1407 [
e08fae02 1408 // 4 weeks after first email send first repeat
861d11c4 1409 'time' => '2012-09-12 01:00:00',
9099cab3
CW
1410 'recipients' => [['test-member@example.com']],
1411 ],
1412 [
e08fae02
PH
1413 // 1 week after first repeat send nothing
1414 // There was a bug where the first repeat went out and then
1415 // it would keep going out every cron run. This is to check that's
1416 // not happening.
1417 'time' => '2012-09-19 01:00:00',
9099cab3
CW
1418 'recipients' => [],
1419 ],
1420 [
e08fae02
PH
1421 // 4 weeks after first repeat send second repeat
1422 'time' => '2012-10-10 01:00:00',
9099cab3
CW
1423 'recipients' => [['test-member@example.com']],
1424 ],
1425 [
e08fae02
PH
1426 // 4 months after membership end, send nothing
1427 'time' => '2012-10-15 01:00:00',
9099cab3
CW
1428 'recipients' => [],
1429 ],
1430 [
e08fae02
PH
1431 // 5 months after membership end, send nothing
1432 'time' => '2012-11-15 01:00:00',
9099cab3
CW
1433 'recipients' => [],
1434 ],
1435 ]);
861d11c4
DG
1436 }
1437
1438 /**
442cf836
EM
1439 * Test behaviour when date changes.
1440 *
861d11c4
DG
1441 * Test that the first notification is sent but the second is NOT sent if the end date changes in
1442 * between
1443 * see CRM-15376
34662dda 1444 *
5bcf6842 1445 * @throws \API_Exception
34662dda 1446 * @throws \CRM_Core_Exception
56ed359c 1447 * @throws \CiviCRM_API3_Exception
861d11c4 1448 */
5bcf6842 1449 public function testMembershipEndDateRepeatChangedEndDate_CRM_15376(): void {
861d11c4 1450 // creates membership with end_date = 20120615
34662dda 1451 $membership = $this->createMembershipFromFixture('rolling_membership', 'Current');
9099cab3 1452 $this->callAPISuccess('Email', 'create', [
56ed359c 1453 'contact_id' => $membership['contact_id'],
861d11c4 1454 'email' => 'test-member@example.com',
9099cab3 1455 ]);
56ed359c 1456 $this->callAPISuccess('contact', 'create', array_merge($this->fixtures['contact'], ['contact_id' => $membership['contact_id']]));
861d11c4 1457
56ed359c 1458 $this->createScheduleFromFixtures('sched_membership_end_2month_repeat_twice_4_weeks', ['entity_value' => $membership['membership_type_id']]);
861d11c4 1459 // end_date=2012-06-15 ; schedule is 2 weeks before end_date
9099cab3
CW
1460 $this->assertCronRuns([
1461 [
567b2076 1462 // After the 2-week mark, send an email.
861d11c4 1463 'time' => '2012-08-15 01:00:00',
9099cab3
CW
1464 'recipients' => [['test-member@example.com']],
1465 ],
1466 ]);
861d11c4 1467
442cf836 1468 // Extend membership - reminder should NOT go out.
56ed359c 1469 $this->callAPISuccess('membership', 'create', ['id' => $membership['id'], 'end_date' => '2014-01-01']);
9099cab3
CW
1470 $this->assertCronRuns([
1471 [
442cf836 1472 // After the 2-week mark, send an email.
861d11c4 1473 'time' => '2012-09-12 01:00:00',
9099cab3
CW
1474 'recipients' => [],
1475 ],
1476 ]);
861d11c4
DG
1477 }
1478
6a488035 1479 /**
442cf836
EM
1480 * Test membership end date email sends.
1481 *
6a488035
TO
1482 * For contacts/members which match schedule based on end date,
1483 * an email should be sent.
34662dda 1484 *
991cd69b 1485 * @throws \API_Exception
34662dda 1486 * @throws \CRM_Core_Exception
56ed359c 1487 * @throws \CiviCRM_API3_Exception
6a488035 1488 */
5bcf6842 1489 public function testMembershipEndDateMatch(): void {
6a488035 1490 // creates membership with end_date = 20120615
34662dda 1491 $membership = $this->createMembershipFromFixture('rolling_membership', 'Current');
9099cab3 1492 $this->callAPISuccess('Email', 'create', [
56ed359c 1493 'contact_id' => $membership['contact_id'],
6a488035 1494 'email' => 'test-member@example.com',
9099cab3 1495 ]);
56ed359c 1496 $this->callAPISuccess('contact', 'create', array_merge($this->fixtures['contact'], ['contact_id' => $membership['contact_id']]));
6a488035 1497
56ed359c 1498 $this->createScheduleFromFixtures('sched_membership_end_2week', ['entity_value' => $membership['membership_type_id']]);
6a488035
TO
1499
1500 // end_date=2012-06-15 ; schedule is 2 weeks before end_date
9099cab3
CW
1501 $this->assertCronRuns([
1502 [
442cf836 1503 // Before the 2-week mark, no email.
6a488035 1504 'time' => '2012-05-31 01:00:00',
9099cab3
CW
1505 'recipients' => [],
1506 ],
1507 [
442cf836 1508 // After the 2-week mark, send an email.
6a488035 1509 'time' => '2012-06-01 01:00:00',
9099cab3
CW
1510 'recipients' => [['test-member@example.com']],
1511 ],
1512 [
e08fae02
PH
1513 // After the email is sent, another one is not sent
1514 'time' => '2012-06-01 02:00:00',
9099cab3
CW
1515 'recipients' => [],
1516 ],
1517 ]);
d2868251 1518
1519 // Now suppose user has renewed for rolling membership after 3 months, so upcoming assertion is written
1520 // to ensure that new reminder is sent 2 week before the new end_date i.e. '2012-09-15'
56ed359c
EM
1521 $membershipBAO = new CRM_Member_BAO_Membership();
1522 $membershipBAO->id = $membership['id'];
1523 $membershipBAO->end_date = '2012-09-15';
1524 $membershipBAO->save();
d2868251 1525
1526 //change the email id of chosen membership contact to assert
1527 //recipient of not the previously sent mail but the new one
9099cab3 1528 $result = $this->callAPISuccess('Email', 'create', [
d2868251 1529 'is_primary' => 1,
56ed359c 1530 'contact_id' => $membership['contact_id'],
cbcb7579 1531 'email' => 'member2@example.com',
9099cab3 1532 ]);
d2868251 1533 $this->assertAPISuccess($result);
1534
1535 // end_date=2012-09-15 ; schedule is 2 weeks before end_date
9099cab3
CW
1536 $this->assertCronRuns([
1537 [
cbcb7579 1538 // Before the 2-week mark, no email
d2868251 1539 'time' => '2012-08-31 01:00:00',
9099cab3
CW
1540 'recipients' => [],
1541 ],
1542 [
e08fae02
PH
1543 // After the 2-week mark, send an email
1544 'time' => '2012-09-01 01:00:00',
9099cab3
CW
1545 'recipients' => [['member2@example.com']],
1546 ],
1547 [
e08fae02
PH
1548 // After the email is sent, another one is not sent
1549 'time' => '2012-09-01 02:00:00',
9099cab3
CW
1550 'recipients' => [],
1551 ],
1552 ]);
56ed359c
EM
1553 $membershipBAO = new CRM_Member_BAO_Membership();
1554 $membershipBAO->id = $membership['id'];
1555 $membershipBAO->end_date = '2012-12-15';
1556 $membershipBAO->save();
e08fae02 1557 // end_date=2012-12-15 ; schedule is 2 weeks before end_date
9099cab3
CW
1558 $this->assertCronRuns([
1559 [
e08fae02
PH
1560 // Before the 2-week mark, no email
1561 'time' => '2012-11-30 01:00:00',
9099cab3
CW
1562 'recipients' => [],
1563 ],
1564 [
e08fae02
PH
1565 // After the 2-week mark, send an email
1566 'time' => '2012-12-01 01:00:00',
9099cab3
CW
1567 'recipients' => [['member2@example.com']],
1568 ],
1569 [
e08fae02
PH
1570 // After the email is sent, another one is not sent
1571 'time' => '2012-12-01 02:00:00',
9099cab3
CW
1572 'recipients' => [],
1573 ],
1574 ]);
e08fae02
PH
1575
1576 }
1577
e08fae02
PH
1578 /**
1579 * This test is very similar to testMembershipEndDateMatch, but it adds
1580 * another contact because there was a bug in
1581 * RecipientBuilder::buildRelFirstPass where it was only sending the
1582 * reminder for the first contact returned in a query for renewed
1583 * memberships. Other contacts wouldn't get the mail.
34662dda 1584 *
56ed359c 1585 * @throws \API_Exception
34662dda 1586 * @throws \CRM_Core_Exception
56ed359c 1587 * @throws \CiviCRM_API3_Exception
e08fae02 1588 */
5bcf6842 1589 public function testMultipleMembershipEndDateMatch(): void {
56ed359c
EM
1590 $contactID = $this->callAPISuccess('Contact', 'create', array_merge($this->fixtures['contact'], ['email' => 'test-member@example.com']))['id'];
1591 $contactID2 = $this->callAPISuccess('Contact', 'create', $this->fixtures['contact_2'])['id'];
1592 $membershipOne = $this->createMembershipFromFixture('rolling_membership', 2, [], ['contact_id' => $contactID]);
1593 $membershipTypeId = $membershipOne['membership_type_id'];
1594 $membershipTwo = $this->createMembershipFromFixture('rolling_membership', 2, [], ['contact_id' => $contactID2, 'membership_type_id' => $membershipTypeId]);
1595 // We are using dates that 'should' be expired but the test expects them not to be
1596 CRM_Core_DAO::executeQuery('UPDATE civicrm_membership SET status_id = 2 WHERE 1');
c5e1bf98 1597 $this->createScheduleFromFixtures('sched_membership_end_2week', ['entity_value' => $membershipTypeId]);
e08fae02
PH
1598
1599 // end_date=2012-06-15 ; schedule is 2 weeks before end_date
9099cab3
CW
1600 $this->assertCronRuns([
1601 [
e08fae02
PH
1602 // Before the 2-week mark, no email.
1603 'time' => '2012-05-31 01:00:00',
9099cab3
CW
1604 'recipients' => [],
1605 ],
1606 [
e08fae02
PH
1607 // After the 2-week mark, send emails.
1608 'time' => '2012-06-01 01:00:00',
9099cab3
CW
1609 'recipients' => [
1610 ['test-member@example.com'],
1611 ['test-contact-2@example.com'],
1612 ],
1613 ],
1614 [
e08fae02
PH
1615 // After the email is sent, another one is not sent
1616 'time' => '2012-06-01 02:00:00',
9099cab3
CW
1617 'recipients' => [],
1618 ],
1619 ]);
e08fae02
PH
1620
1621 // Now suppose user has renewed for rolling membership after 3 months, so upcoming assertion is written
1622 // to ensure that new reminder is sent 2 week before the new end_date i.e. '2012-09-15'
56ed359c
EM
1623 $membershipOneBAO = new CRM_Member_BAO_Membership();
1624 $membershipOneBAO->id = $membershipOne['id'];
1625 $membershipOneBAO->end_date = '2012-09-15';
1626 $membershipOneBAO->save();
1627 $membershipTwoBAO = new CRM_Member_BAO_Membership();
1628 $membershipTwoBAO->id = $membershipTwo['id'];
1629 $membershipTwoBAO->end_date = '2012-09-15';
1630 $membershipTwoBAO->save();
e08fae02
PH
1631
1632 // end_date=2012-09-15 ; schedule is 2 weeks before end_date
9099cab3
CW
1633 $this->assertCronRuns([
1634 [
e08fae02
PH
1635 // Before the 2-week mark, no email
1636 'time' => '2012-08-31 01:00:00',
9099cab3
CW
1637 'recipients' => [],
1638 ],
1639 [
e08fae02
PH
1640 // After the 2-week mark, send an email
1641 'time' => '2012-09-01 01:00:00',
9099cab3
CW
1642 'recipients' => [
1643 ['test-member@example.com'],
1644 ['test-contact-2@example.com'],
1645 ],
1646 ],
1647 [
e08fae02
PH
1648 // After the email is sent, another one is not sent
1649 'time' => '2012-06-01 02:00:00',
9099cab3
CW
1650 'recipients' => [],
1651 ],
1652 ]);
6a488035
TO
1653 }
1654
4b1efa1d 1655 /**
442cf836
EM
1656 * Test membership end date email.
1657 *
6c6e6187
TO
1658 * For contacts/members which match schedule based on end date,
1659 * an email should be sent.
34662dda 1660 *
5bcf6842 1661 * @throws \API_Exception
34662dda 1662 * @throws \CRM_Core_Exception
56ed359c 1663 * @throws \CiviCRM_API3_Exception
6c6e6187 1664 */
5bcf6842 1665 public function testMembershipEndDateNoMatch(): void {
4b1efa1d 1666 // creates membership with end_date = 20120615
34662dda 1667 $membership = $this->createMembershipFromFixture('rolling_membership', 'Grace');
1668 $this->callAPISuccess('Email', 'create', [
56ed359c 1669 'contact_id' => $membership['contact_id'],
4b1efa1d 1670 'email' => 'test-member@example.com',
9099cab3 1671 ]);
56ed359c
EM
1672 $this->callAPISuccess('contact', 'create', array_merge($this->fixtures['contact'], ['contact_id' => $membership['contact_id']]));
1673 $this->createScheduleFromFixtures('sched_membership_end_2month', ['entity_value' => $membership['membership_type_id']]);
4b1efa1d 1674
1675 // end_date=2012-06-15 ; schedule is 2 weeks before end_date
9099cab3
CW
1676 $this->assertCronRuns([
1677 [
442cf836 1678 // Before the 2-week mark, no email.
4b1efa1d 1679 'time' => '2012-05-31 01:00:00',
9099cab3
CW
1680 'recipients' => [],
1681 ],
1682 [
e08fae02 1683 // After the 2-week mark, no email
4b1efa1d 1684 'time' => '2013-05-01 01:00:00',
9099cab3
CW
1685 'recipients' => [],
1686 ],
1687 ]);
4b1efa1d 1688 }
1689
34662dda 1690 /**
1691 * @throws \CRM_Core_Exception
1692 */
c5e1bf98 1693 public function testContactBirthDateNoAnniversary(): void {
bf308d29 1694 $contact = $this->callAPISuccess('Contact', 'create', $this->fixtures['contact_birthdate']);
43030baf 1695 $this->_testObjects['CRM_Contact_DAO_Contact'][] = $contact['id'];
c5e1bf98 1696 $this->createScheduleFromFixtures('sched_contact_birth_day_yesterday');
9099cab3
CW
1697 $this->assertCronRuns([
1698 [
442cf836 1699 // On the birthday, no email.
43030baf 1700 'time' => '2005-07-07 01:00:00',
9099cab3
CW
1701 'recipients' => [],
1702 ],
1703 [
442cf836 1704 // The next day, send an email.
43030baf 1705 'time' => '2005-07-08 20:00:00',
c5e1bf98 1706 'recipients' => [['test-birth_day@example.com']],
9099cab3
CW
1707 ],
1708 ]);
43030baf
AH
1709 }
1710
34662dda 1711 /**
1712 * @throws \CRM_Core_Exception
1713 */
5bcf6842 1714 public function testContactBirthDateAnniversary(): void {
6c6e6187 1715 $contact = $this->callAPISuccess('Contact', 'create', $this->fixtures['contact_birthdate']);
43030baf 1716 $this->_testObjects['CRM_Contact_DAO_Contact'][] = $contact['id'];
c5e1bf98 1717 $this->createScheduleFromFixtures('sched_contact_birth_day_anniversary');
9099cab3
CW
1718 $this->assertCronRuns([
1719 [
92c99a4a 1720 // On some random day, no email.
43030baf 1721 'time' => '2014-03-07 01:00:00',
9099cab3
CW
1722 'recipients' => [],
1723 ],
1724 [
92c99a4a 1725 // On the eve of their 9th birthday, send an email.
43030baf 1726 'time' => '2014-07-06 20:00:00',
c5e1bf98 1727 'recipients' => [['test-birth_day@example.com']],
9099cab3
CW
1728 ],
1729 ]);
43030baf 1730 }
4b1efa1d 1731
34662dda 1732 /**
1733 * @throws \CRM_Core_Exception
1734 */
5bcf6842 1735 public function testContactCustomDateNoAnniversary(): void {
9099cab3 1736 $group = [
43030baf
AH
1737 'title' => 'Test_Group',
1738 'name' => 'test_group',
9099cab3 1739 'extends' => ['Individual'],
43030baf 1740 'style' => 'Inline',
6c6e6187 1741 'is_multiple' => FALSE,
43030baf 1742 'is_active' => 1,
9099cab3 1743 ];
bf308d29 1744 $createGroup = $this->callAPISuccess('custom_group', 'create', $group);
9099cab3 1745 $field = [
43030baf
AH
1746 'label' => 'Graduation',
1747 'data_type' => 'Date',
1748 'html_type' => 'Select Date',
1749 'custom_group_id' => $createGroup['id'],
9099cab3 1750 ];
bf308d29 1751 $createField = $this->callAPISuccess('custom_field', 'create', $field);
43030baf
AH
1752 $contactParams = $this->fixtures['contact'];
1753 $contactParams["custom_{$createField['id']}"] = '2013-12-16';
bf308d29 1754 $contact = $this->callAPISuccess('Contact', 'create', $contactParams);
43030baf 1755 $this->_testObjects['CRM_Contact_DAO_Contact'][] = $contact['id'];
c5e1bf98 1756 $this->createScheduleFromFixtures('sched_contact_grad_tomorrow', ['entity_value' => "custom_{$createField['id']}"]);
9099cab3
CW
1757 $this->assertCronRuns([
1758 [
92c99a4a 1759 // On some random day, no email.
43030baf 1760 'time' => '2014-03-07 01:00:00',
9099cab3
CW
1761 'recipients' => [],
1762 ],
1763 [
92c99a4a 1764 // On the eve of their graduation, send an email.
43030baf 1765 'time' => '2013-12-15 20:00:00',
9099cab3
CW
1766 'recipients' => [['test-member@example.com']],
1767 ],
1768 ]);
1769 $this->callAPISuccess('custom_group', 'delete', ['id' => $createGroup['id']]);
43030baf 1770 }
92915c55 1771
34662dda 1772 /**
1773 * @throws \CRM_Core_Exception
1774 */
5bcf6842 1775 public function testContactCreatedNoAnniversary(): void {
4aff0253 1776 $contact = $this->callAPISuccess('Contact', 'create', $this->fixtures['contact_birthdate']);
34662dda 1777 $this->createScheduleFromFixtures('sched_contact_created_yesterday');
9099cab3
CW
1778 $this->assertCronRuns([
1779 [
4aff0253
AH
1780 // On the date created, no email.
1781 'time' => $contact['values'][$contact['id']]['created_date'],
9099cab3
CW
1782 'recipients' => [],
1783 ],
1784 [
4aff0253
AH
1785 // The next day, send an email.
1786 'time' => date('Y-m-d H:i:s', strtotime($contact['values'][$contact['id']]['created_date'] . ' +1 day')),
3cf02d63 1787 'recipients' => [['test-birth_day@example.com'], ['fixme.domainemail@example.org'], ['domainemail2@example.org']],
9099cab3
CW
1788 ],
1789 ]);
4aff0253
AH
1790 }
1791
34662dda 1792 /**
1793 * Test the impact of changing the anniversary.
1794 *
1795 * @throws \CRM_Core_Exception
1796 */
5bcf6842 1797 public function testContactModifiedAnniversary(): void {
4aff0253
AH
1798 $contact = $this->callAPISuccess('Contact', 'create', $this->fixtures['contact_birthdate']);
1799 $this->_testObjects['CRM_Contact_DAO_Contact'][] = $contact['id'];
9099cab3 1800 $modifiedDate = $this->callAPISuccess('Contact', 'getvalue', ['id' => $contact['id'], 'return' => 'modified_date']);
c5e1bf98 1801 $this->createScheduleFromFixtures('sched_contact_mod_anniversary');
9099cab3
CW
1802 $this->assertCronRuns([
1803 [
4aff0253 1804 // On some random day, no email.
a9ac9f6c 1805 'time' => date('Y-m-d H:i:s', strtotime($contact['values'][$contact['id']]['modified_date'] . ' -60 days')),
9099cab3
CW
1806 'recipients' => [],
1807 ],
1808 [
4aff0253 1809 // On the eve of 3 years after they were modified, send an email.
688bd604 1810 'time' => date('Y-m-d H:i:s', strtotime($modifiedDate . ' +3 years -1 day')),
3cf02d63 1811 'recipients' => [['test-birth_day@example.com'], ['fixme.domainemail@example.org'], ['domainemail2@example.org']],
9099cab3
CW
1812 ],
1813 ]);
4aff0253
AH
1814 }
1815
baa85770 1816 /**
56ed359c
EM
1817 * Check that limit_to + an empty recipients doesn't sent to multiple
1818 * contacts.
34662dda 1819 *
1820 * @throws \API_Exception
1821 * @throws \CRM_Core_Exception
56ed359c 1822 * @throws \CiviCRM_API3_Exception
baa85770 1823 */
5bcf6842 1824 public function testMembershipLimitToNone(): void {
baa85770 1825 // creates membership with end_date = 20120615
34662dda 1826 $membership = $this->createMembershipFromFixture('rolling_membership', 'Current');
9099cab3 1827 $result = $this->callAPISuccess('Email', 'create', [
56ed359c 1828 'contact_id' => $membership['contact_id'],
baa85770 1829 'email' => 'member@example.com',
9099cab3 1830 ]);
56ed359c 1831 $this->callAPISuccess('contact', 'create', array_merge($this->fixtures['contact'], ['contact_id' => $membership['contact_id']]));
9099cab3 1832 $this->callAPISuccess('contact', 'create', ['email' => 'b@c.com', 'contact_type' => 'Individual']);
baa85770
EM
1833
1834 $this->assertAPISuccess($result);
1835
56ed359c 1836 $this->createScheduleFromFixtures('sched_membership_end_limit_to_none', ['entity_value' => $membership['membership_type_id']]);
baa85770
EM
1837
1838 // end_date=2012-06-15 ; schedule is 2 weeks before end_date
9099cab3
CW
1839 $this->assertCronRuns([
1840 [
92c99a4a 1841 // Before the 2-week mark, no email.
baa85770 1842 'time' => '2012-05-31 01:00:00',
9099cab3
CW
1843 'recipients' => [],
1844 ],
1845 ]);
baa85770
EM
1846 }
1847
34662dda 1848 /**
1849 * Test handling of reference date for memberships.
1850 *
1851 * @throws \API_Exception
1852 * @throws \CRM_Core_Exception
56ed359c 1853 * @throws \CiviCRM_API3_Exception
34662dda 1854 */
5bcf6842 1855 public function testMembershipWithReferenceDate(): void {
34662dda 1856 $membership = $this->createMembershipFromFixture('rolling_membership', 'Current', ['email' => 'member@example.com']);
56ed359c 1857 $this->callAPISuccess('contact', 'create', array_merge($this->fixtures['contact'], ['contact_id' => $membership['contact_id']]));
5c4d6559 1858
56ed359c 1859 $this->createScheduleFromFixtures('sched_membership_join_2week', ['entity_value' => $membership['membership_type_id']]);
5c4d6559 1860
1861 // start_date=2012-03-15 ; schedule is 2 weeks after start_date
9099cab3
CW
1862 $this->assertCronRuns([
1863 [
cbcb7579 1864 // After the 2-week mark, send an email
5c4d6559 1865 'time' => '2012-03-29 01:00:00',
9099cab3
CW
1866 'recipients' => [['member@example.com']],
1867 ],
1868 [
cbcb7579 1869 // After the 2-week 1day mark, don't send an email
9c0fe051 1870 'time' => '2012-03-30 01:00:00',
9099cab3
CW
1871 'recipients' => [],
1872 ],
1873 ]);
5c4d6559 1874
1875 //check if reference date is set to membership's join date
1876 //as per the action_start_date chosen for current schedule reminder
e08fae02 1877 $this->assertEquals('2012-03-15 00:00:00',
56ed359c 1878 CRM_Core_DAO::getFieldValue('CRM_Core_DAO_ActionLog', $membership['contact_id'], 'reference_date', 'contact_id')
5c4d6559 1879 );
1880
e7c6a902 1881 //change current membership join date that may signifies as membership renewal activity
56ed359c
EM
1882 $membershipBAO = new CRM_Member_BAO_Membership();
1883 $membershipBAO->id = $membership['id'];
1884 $membershipBAO->join_date = '2012-03-29';
1885 $membershipBAO->save();
5c4d6559 1886
9099cab3
CW
1887 $this->assertCronRuns([
1888 [
cbcb7579 1889 // After the 13 days of the changed join date 2012-03-29, don't send an email
9c0fe051 1890 'time' => '2012-04-11 01:00:00',
9099cab3
CW
1891 'recipients' => [],
1892 ],
1893 [
cbcb7579 1894 // After the 2-week of the changed join date 2012-03-29, send an email
5c4d6559 1895 'time' => '2012-04-12 01:00:00',
9099cab3
CW
1896 'recipients' => [['member@example.com']],
1897 ],
1898 ]);
1899 $this->assertCronRuns([
1900 [
7d48c97b 1901 // It should not re-send on the same day
1902 'time' => '2012-04-12 01:00:00',
9099cab3
CW
1903 'recipients' => [],
1904 ],
1905 ]);
5c4d6559 1906 }
baa85770 1907
34662dda 1908 /**
1909 * Test multiple membership reminder.
1910 *
1911 * @throws \API_Exception
1912 * @throws \CRM_Core_Exception
56ed359c 1913 * @throws \CiviCRM_API3_Exception
34662dda 1914 */
5bcf6842 1915 public function testMembershipOnMultipleReminder(): void {
34662dda 1916 $membership = $this->createMembershipFromFixture('rolling_membership', 'Current', ['email' => 'member@example.com']);
56ed359c 1917 $this->callAPISuccess('contact', 'create', array_merge($this->fixtures['contact'], ['contact_id' => $membership['contact_id']]));
d2868251 1918
39b959db
SL
1919 // Send email 2 weeks before end_date
1920 $actionScheduleBefore = $this->fixtures['sched_membership_end_2week'];
1921 // Send email on end_date/expiry date
1922 $actionScheduleOn = $this->fixtures['sched_on_membership_end_date'];
1923 // Send email 1 day after end_date/grace period
1924 $actionScheduleAfter = $this->fixtures['sched_after_1day_membership_end_date'];
56ed359c 1925 $actionScheduleBefore['entity_value'] = $actionScheduleOn['entity_value'] = $actionScheduleAfter['entity_value'] = $membership['membership_type_id'];
9099cab3 1926 foreach (['actionScheduleBefore', 'actionScheduleOn', 'actionScheduleAfter'] as $value) {
d2868251 1927 $$value = CRM_Core_BAO_ActionSchedule::add($$value);
d2868251 1928 }
1929
1930 $this->assertCronRuns(
9099cab3
CW
1931 [
1932 [
cbcb7579 1933 // 1day 2weeks before membership end date(MED), don't send mail
d2868251 1934 'time' => '2012-05-31 01:00:00',
9099cab3
CW
1935 'recipients' => [],
1936 ],
1937 [
cbcb7579 1938 // 2 weeks before MED, send an email
d2868251 1939 'time' => '2012-06-01 01:00:00',
9099cab3
CW
1940 'recipients' => [['member@example.com']],
1941 ],
1942 [
cbcb7579 1943 // 1day before MED, don't send mail
d2868251 1944 'time' => '2012-06-14 01:00:00',
9099cab3
CW
1945 'recipients' => [],
1946 ],
1947 [
cbcb7579 1948 // On MED, send an email
d2868251 1949 'time' => '2012-06-15 00:00:00',
9099cab3
CW
1950 'recipients' => [['member@example.com']],
1951 ],
1952 [
cbcb7579 1953 // After 1day of MED, send an email
d2868251 1954 'time' => '2012-06-16 01:00:00',
9099cab3
CW
1955 'recipients' => [['member@example.com']],
1956 ],
1957 [
cbcb7579 1958 // After 1day 1min of MED, don't send an email
d2868251 1959 'time' => '2012-06-17 00:01:00',
9099cab3
CW
1960 'recipients' => [],
1961 ],
1962 ]
cbcb7579 1963 );
d2868251 1964
1965 // Assert the timestamp as of when the emails of respective three reminders as configured
1966 // 2 weeks before, on and 1 day after MED, are sent
f42ab010
TO
1967 $this->assertApproxEquals(
1968 strtotime('2012-06-01 01:00:00'),
1969 strtotime(CRM_Core_DAO::getFieldValue('CRM_Core_DAO_ActionLog', $actionScheduleBefore->id, 'action_date_time', 'action_schedule_id', TRUE)),
39b959db
SL
1970 // Variation in test execution time.
1971 3
f42ab010
TO
1972 );
1973 $this->assertApproxEquals(
1974 strtotime('2012-06-15 00:00:00'),
1975 strtotime(CRM_Core_DAO::getFieldValue('CRM_Core_DAO_ActionLog', $actionScheduleOn->id, 'action_date_time', 'action_schedule_id', TRUE)),
39b959db
SL
1976 // Variation in test execution time.
1977 3
f42ab010
TO
1978 );
1979 $this->assertApproxEquals(
1980 strtotime('2012-06-16 01:00:00'),
1981 strtotime(CRM_Core_DAO::getFieldValue('CRM_Core_DAO_ActionLog', $actionScheduleAfter->id, 'action_date_time', 'action_schedule_id', TRUE)),
39b959db
SL
1982 // Variation in test execution time.
1983 3
f42ab010 1984 );
d2868251 1985
e7c6a902 1986 //extend MED to 2 weeks after the current MED (that may signifies as membership renewal activity)
d2868251 1987 // and lets assert as of when the new set of reminders will be sent against their respective Schedule Reminders(SR)
56ed359c
EM
1988 $membershipBAO = new CRM_Member_BAO_Membership();
1989 $membershipBAO->id = $membership['id'];
1990 $membershipBAO->end_date = '2012-06-20';
1991 $membershipBAO->save();
d2868251 1992
56ed359c 1993 $this->callAPISuccess('Contact', 'get', ['id' => $membership['contact_id']]);
d2868251 1994 $this->assertCronRuns(
9099cab3
CW
1995 [
1996 [
cbcb7579 1997 // 1day 2weeks before membership end date(MED), don't send mail
9c0fe051 1998 'time' => '2012-06-05 01:00:00',
9099cab3
CW
1999 'recipients' => [],
2000 ],
2001 [
cbcb7579 2002 // 2 weeks before MED, send an email
9c0fe051 2003 'time' => '2012-06-06 01:00:00',
9099cab3
CW
2004 'recipients' => [['member@example.com']],
2005 ],
2006 [
cbcb7579 2007 // 1day before MED, don't send mail
9c0fe051 2008 'time' => '2012-06-19 01:00:00',
9099cab3
CW
2009 'recipients' => [],
2010 ],
2011 [
cbcb7579 2012 // On MED, send an email
9c0fe051 2013 'time' => '2012-06-20 00:00:00',
9099cab3
CW
2014 'recipients' => [['member@example.com']],
2015 ],
2016 [
cbcb7579 2017 // After 1day of MED, send an email
9c0fe051 2018 'time' => '2012-06-21 01:00:00',
9099cab3
CW
2019 'recipients' => [['member@example.com']],
2020 ],
2021 [
cbcb7579 2022 // After 1day 1min of MED, don't send an email
9c0fe051 2023 'time' => '2012-07-21 00:01:00',
9099cab3
CW
2024 'recipients' => [],
2025 ],
2026 ]);
d2868251 2027 }
2028
34662dda 2029 /**
2030 * Test reminders sent on custom data anniversary.
2031 *
98902dea 2032 * @throws \API_Exception
34662dda 2033 * @throws \CRM_Core_Exception
2034 */
5bcf6842 2035 public function testContactCustomDate_Anniversary(): void {
98902dea 2036 $this->createCustomGroupWithFieldOfType([], 'date');
43030baf 2037 $contactParams = $this->fixtures['contact'];
98902dea 2038 $contactParams[$this->getCustomFieldName('date')] = '2013-12-16';
bf308d29 2039 $contact = $this->callAPISuccess('Contact', 'create', $contactParams);
43030baf 2040 $this->_testObjects['CRM_Contact_DAO_Contact'][] = $contact['id'];
98902dea 2041 $this->fixtures['sched_contact_grad_anniversary']['entity_value'] = $this->getCustomFieldName('date');
c5e1bf98 2042 $this->createScheduleFromFixtures('sched_contact_grad_anniversary');
2043
9099cab3
CW
2044 $this->assertCronRuns([
2045 [
92c99a4a 2046 // On some random day, no email.
43030baf 2047 'time' => '2014-03-07 01:00:00',
9099cab3
CW
2048 'recipients' => [],
2049 ],
2050 [
92c99a4a 2051 // A week after their 5th anniversary of graduation, send an email.
43030baf 2052 'time' => '2018-12-23 20:00:00',
9099cab3
CW
2053 'recipients' => [['test-member@example.com']],
2054 ],
2055 ]);
43030baf 2056 }
4b1efa1d 2057
76ea51c7
JP
2058 /**
2059 * Test sched reminder set via registration date.
34662dda 2060 *
2061 * @throws \CRM_Core_Exception
5bcf6842 2062 * @throws \CiviCRM_API3_Exception
76ea51c7 2063 */
5bcf6842 2064 public function testEventTypeRegistrationDate(): void {
34662dda 2065 $contact = $this->individualCreate(['email' => 'test-event@example.com']);
76ea51c7 2066 //Add it as a participant to an event ending registration - 7 days from now.
9099cab3 2067 $params = [
76ea51c7
JP
2068 'start_date' => date('Ymd', strtotime('-5 day')),
2069 'end_date' => date('Ymd', strtotime('+7 day')),
2070 'registration_start_date' => date('Ymd', strtotime('-5 day')),
2071 'registration_end_date' => date('Ymd', strtotime('+7 day')),
9099cab3 2072 ];
76ea51c7 2073 $event = $this->eventCreate($params);
9099cab3 2074 $this->participantCreate(['contact_id' => $contact, 'event_id' => $event['id']]);
76ea51c7
JP
2075
2076 //Create a scheduled reminder to send email 7 days before registration date.
c5e1bf98 2077 $actionSchedule = $this->fixtures['sched_event_type_start_1week_before'];
76ea51c7
JP
2078 $actionSchedule['start_action_offset'] = 7;
2079 $actionSchedule['start_action_unit'] = 'day';
2080 $actionSchedule['start_action_date'] = 'registration_end_date';
2081 $actionSchedule['entity_value'] = $event['values'][$event['id']]['event_type_id'];
9099cab3 2082 $actionSchedule['entity_status'] = $this->callAPISuccessGetValue('ParticipantStatusType', [
34662dda 2083 'return' => 'id',
2084 'name' => 'Attended',
9099cab3 2085 ]);
899bd155 2086 $actionSched = $this->callAPISuccess('action_schedule', 'create', $actionSchedule);
76ea51c7 2087 //Run the cron and verify if an email was sent.
9099cab3
CW
2088 $this->assertCronRuns([
2089 [
76ea51c7 2090 'time' => date('Y-m-d'),
9099cab3
CW
2091 'recipients' => [['test-event@example.com']],
2092 ],
2093 ]);
899bd155
JP
2094
2095 //Create contact 2
9099cab3 2096 $contactParams = [
899bd155 2097 'email' => 'test-event2@example.com',
9099cab3 2098 ];
899bd155
JP
2099 $contact2 = $this->individualCreate($contactParams);
2100 //Create an event with registration end date = 2 week from now.
2101 $params['end_date'] = date('Ymd', strtotime('+2 week'));
2102 $params['registration_end_date'] = date('Ymd', strtotime('+2 week'));
2103 $event2 = $this->eventCreate($params);
9099cab3 2104 $this->participantCreate(['contact_id' => $contact2, 'event_id' => $event2['id']]);
899bd155
JP
2105
2106 //Assert there is no reminder sent to the contact.
9099cab3
CW
2107 $this->assertCronRuns([
2108 [
899bd155 2109 'time' => date('Y-m-d'),
9099cab3
CW
2110 'recipients' => [],
2111 ],
2112 ]);
899bd155
JP
2113
2114 //Modify the sched reminder to be sent 2 week from registration end date.
9099cab3 2115 $this->callAPISuccess('action_schedule', 'create', [
899bd155
JP
2116 'id' => $actionSched['id'],
2117 'start_action_offset' => 2,
2118 'start_action_unit' => 'week',
9099cab3 2119 ]);
899bd155
JP
2120
2121 //Contact should receive the reminder now.
9099cab3
CW
2122 $this->assertCronRuns([
2123 [
899bd155 2124 'time' => date('Y-m-d'),
9099cab3
CW
2125 'recipients' => [['test-event2@example.com']],
2126 ],
2127 ]);
76ea51c7
JP
2128 }
2129
2130 /**
2131 * Test sched reminder set via start date.
34662dda 2132 *
2133 * @throws \CRM_Core_Exception
56ed359c 2134 * @throws \CiviCRM_API3_Exception
76ea51c7 2135 */
5bcf6842 2136 public function testEventTypeStartDate(): void {
d3eae6ea 2137 // Create event+participant with start_date = 20120315, end_date = 20120615.
56ed359c
EM
2138 $params = $this->fixtures['participant'];
2139 $params['event_id'] = $this->callAPISuccess('Event', 'create', array_merge($this->fixtures['participant']['event_id'], ['event_type_id' => 1]))['id'];
2140 $params['status_id'] = 2;
2141 $params['contact_id'] = $this->individualCreate(array_merge($this->fixtures['contact'], ['email' => 'test-event@example.com']));
2142 $this->callAPISuccess('Participant', 'create', $params);
d3eae6ea 2143
c5e1bf98 2144 $actionSchedule = $this->fixtures['sched_event_type_start_1week_before'];
56ed359c 2145 $actionSchedule['entity_value'] = CRM_Core_DAO::getFieldValue('CRM_Event_DAO_Event', $params['event_id'], 'event_type_id');
d3eae6ea
TO
2146 $this->callAPISuccess('action_schedule', 'create', $actionSchedule);
2147
d3eae6ea 2148 // end_date=2012-06-15 ; schedule is 2 weeks before end_date
9099cab3
CW
2149 $this->assertCronRuns([
2150 [
d3eae6ea
TO
2151 // 2 weeks before
2152 'time' => '2012-03-02 01:00:00',
9099cab3
CW
2153 'recipients' => [],
2154 ],
2155 [
d3eae6ea
TO
2156 // 1 week before
2157 'time' => '2012-03-08 01:00:00',
9099cab3
CW
2158 'recipients' => [['test-event@example.com']],
2159 ],
2160 [
d3eae6ea
TO
2161 // And then nothing else
2162 'time' => '2012-03-16 01:00:00',
9099cab3
CW
2163 'recipients' => [],
2164 ],
2165 ]);
d3eae6ea
TO
2166 }
2167
34662dda 2168 /**
2169 * Test schedule on event end date.
2170 *
2171 * @throws \CRM_Core_Exception
2172 */
5bcf6842 2173 public function testEventTypeEndDateRepeat(): void {
d3eae6ea 2174 // Create event+participant with start_date = 20120315, end_date = 20120615.
9099cab3
CW
2175 $participant = $this->createTestObject('CRM_Event_DAO_Participant', array_merge($this->fixtures['participant'], ['status_id' => 2]));
2176 $this->callAPISuccess('Email', 'create', [
d3eae6ea
TO
2177 'contact_id' => $participant->contact_id,
2178 'email' => 'test-event@example.com',
9099cab3 2179 ]);
34662dda 2180 $this->callAPISuccess('contact', 'create', array_merge($this->fixtures['contact'], ['contact_id' => $participant->contact_id]));
d3eae6ea 2181
c5e1bf98 2182 $actionSchedule = $this->fixtures['sched_event_type_end_2month_repeat_twice_2_weeks'];
d3eae6ea
TO
2183 $actionSchedule['entity_value'] = CRM_Core_DAO::getFieldValue('CRM_Event_DAO_Event', $participant->event_id, 'event_type_id');
2184 $this->callAPISuccess('action_schedule', 'create', $actionSchedule);
2185
9099cab3
CW
2186 $this->assertCronRuns([
2187 [
d3eae6ea
TO
2188 // Almost 2 months.
2189 'time' => '2012-08-13 01:00:00',
9099cab3
CW
2190 'recipients' => [],
2191 ],
2192 [
d3eae6ea
TO
2193 // After the 2-month mark, send an email.
2194 'time' => '2012-08-16 01:00:00',
9099cab3
CW
2195 'recipients' => [['test-event@example.com']],
2196 ],
2197 [
d3eae6ea
TO
2198 // After 2 months and 1 week, don't repeat yet.
2199 'time' => '2012-08-23 02:00:00',
9099cab3
CW
2200 'recipients' => [],
2201 ],
2202 [
d3eae6ea
TO
2203 // After 2 months and 2 weeks
2204 'time' => '2012-08-30 02:00:00',
9099cab3
CW
2205 'recipients' => [['test-event@example.com']],
2206 ],
2207 [
d3eae6ea
TO
2208 // After 2 months and 4 week
2209 'time' => '2012-09-13 02:00:00',
9099cab3
CW
2210 'recipients' => [['test-event@example.com']],
2211 ],
2212 [
d3eae6ea
TO
2213 // After 2 months and 6 weeks
2214 'time' => '2012-09-27 01:00:00',
9099cab3
CW
2215 'recipients' => [],
2216 ],
2217 ]);
d3eae6ea
TO
2218 }
2219
6a488035 2220 /**
92c99a4a 2221 * Run a series of cron jobs and make an assertion about email deliveries.
6a488035 2222 *
16b10e64
CW
2223 * @param array $cronRuns
2224 * array specifying when to run cron and what messages to expect; each item is an array with keys:
2225 * - time: string, e.g. '2012-06-15 21:00:01'
2226 * - recipients: array(array(string)), list of email addresses which should receive messages
34662dda 2227 *
2228 * @throws \CRM_Core_Exception
c5e1bf98 2229 * @noinspection DisconnectedForeachInstructionInspection
6a488035 2230 */
c5e1bf98 2231 public function assertCronRuns(array $cronRuns): void {
6a488035
TO
2232 foreach ($cronRuns as $cronRun) {
2233 CRM_Utils_Time::setTime($cronRun['time']);
9099cab3 2234 $this->callAPISuccess('job', 'send_reminder', []);
6a488035 2235 $this->mut->assertRecipients($cronRun['recipients']);
f8c3594b
TO
2236 if (array_key_exists('subjects', $cronRun)) {
2237 $this->mut->assertSubjects($cronRun['subjects']);
2238 }
6a488035
TO
2239 $this->mut->clearMessages();
2240 }
2241 }
2242
6a488035 2243 /**
1d3260ea 2244 * @var array
202b2c69 2245 *
1d3260ea 2246 * (DAO_Name => array(int)) List of items to garbage-collect during tearDown
6a488035 2247 */
edcbcbb0 2248 private $_testObjects = [];
6a488035
TO
2249
2250 /**
2251 * This is a wrapper for CRM_Core_DAO::createTestObject which tracks
92c99a4a 2252 * created entities and provides for brainless cleanup.
6a488035 2253 *
56ed359c
EM
2254 * However, it is only really brainless when initially writing the code.
2255 * It 'steals and deletes entities that are part of the 'stock build'.
2256 *
2257 * In general this causes weird stuff.
92c99a4a 2258 *
1e1fdcf6
EM
2259 * @param $daoName
2260 * @param array $params
2261 * @param int $numObjects
2262 * @param bool $createOnly
92c99a4a
EM
2263 *
2264 * @return array|NULL|object
56ed359c 2265 * @see CRM_Core_DAO::createTestObject
6a488035 2266 */
56ed359c 2267 public function createTestObject($daoName, array $params = [], int $numObjects = 1, bool $createOnly = FALSE) {
6a488035
TO
2268 $objects = CRM_Core_DAO::createTestObject($daoName, $params, $numObjects, $createOnly);
2269 if (is_array($objects)) {
2270 $this->registerTestObjects($objects);
0db6c3e1
TO
2271 }
2272 else {
9099cab3 2273 $this->registerTestObjects([$objects]);
6a488035
TO
2274 }
2275 return $objects;
2276 }
2277
2278 /**
5a4f6742
CW
2279 * @param array $objects
2280 * DAO or BAO objects.
6a488035 2281 */
c5e1bf98 2282 public function registerTestObjects(array $objects): void {
6a488035
TO
2283 //if (is_object($objects)) {
2284 // $objects = array($objects);
2285 //}
2286 foreach ($objects as $object) {
c5e1bf98 2287 $daoName = str_replace('_BAO_', '_DAO_', get_class($object));
6a488035
TO
2288 $this->_testObjects[$daoName][] = $object->id;
2289 }
2290 }
2291
5bcf6842 2292 public function deleteTestObjects(): void {
6a488035
TO
2293 // Note: You might argue that the FK relations between test
2294 // objects could make this problematic; however, it should
2295 // behave intuitively as long as we mentally split our
2296 // test-objects between the "manual/primary records"
2297 // and the "automatic/secondary records"
2298 foreach ($this->_testObjects as $daoName => $daoIds) {
2299 foreach ($daoIds as $daoId) {
9099cab3 2300 CRM_Core_DAO::deleteTestObjects($daoName, ['id' => $daoId]);
6a488035
TO
2301 }
2302 }
9099cab3 2303 $this->_testObjects = [];
6a488035
TO
2304 }
2305
52f09320
PH
2306 /**
2307 * Test that the various repetition units work correctly.
c5e1bf98 2308 *
0e480632 2309 * @see https://issues.civicrm.org/jira/browse/CRM-17028
c5e1bf98 2310 * @throws \CRM_Core_Exception
52f09320 2311 */
5bcf6842 2312 public function testRepetitionFrequencyUnit(): void {
9099cab3 2313 $membershipTypeParams = [
52f09320
PH
2314 'duration_interval' => '1',
2315 'duration_unit' => 'year',
2316 'is_active' => 1,
2317 'period_type' => 'rolling',
9099cab3 2318 ];
52f09320 2319 $membershipType = $this->createTestObject('CRM_Member_DAO_MembershipType', $membershipTypeParams);
9099cab3 2320 $interval_units = ['hour', 'day', 'week', 'month', 'year'];
52f09320 2321 foreach ($interval_units as $interval_unit) {
c5e1bf98 2322 $membershipEndDate = DateTime::createFromFormat('Y-m-d H:i:s', '2013-03-15 00:00:00');
9099cab3 2323 $contactParams = [
52f09320
PH
2324 'contact_type' => 'Individual',
2325 'first_name' => 'Test',
2326 'last_name' => "Interval $interval_unit",
2327 'is_deceased' => 0,
9099cab3 2328 ];
52f09320 2329 $contact = $this->createTestObject('CRM_Contact_DAO_Contact', $contactParams);
9099cab3 2330 $emailParams = [
52f09320 2331 'contact_id' => $contact->id,
62ca07e7 2332 'is_primary' => 1,
56ed359c 2333 'email' => "test-member-$interval_unit@example.com",
52f09320 2334 'location_type_id' => 1,
9099cab3 2335 ];
c5e1bf98 2336 $this->createTestObject('CRM_Core_DAO_Email', $emailParams);
9099cab3 2337 $membershipParams = [
52f09320
PH
2338 'membership_type_id' => $membershipType->id,
2339 'contact_id' => $contact->id,
2340 'join_date' => '20120315',
2341 'start_date' => '20120315',
2342 'end_date' => '20130315',
2343 'is_override' => 0,
2344 'status_id' => 2,
9099cab3 2345 ];
52f09320
PH
2346 $membershipParams['status-id'] = 1;
2347 $membership = $this->createTestObject('CRM_Member_DAO_Membership', $membershipParams);
2348 $actionScheduleParams = $this->fixtures['sched_on_membership_end_date_repeat_interval'];
2349 $actionScheduleParams['entity_value'] = $membershipType->id;
2350 $actionScheduleParams['repetition_frequency_unit'] = $interval_unit;
2351 $actionScheduleParams['repetition_frequency_interval'] = 2;
2352 $actionSchedule = CRM_Core_BAO_ActionSchedule::add($actionScheduleParams);
52f09320
PH
2353 $beforeEndDate = $this->createModifiedDateTime($membershipEndDate, '-1 day');
2354 $beforeFirstUnit = $this->createModifiedDateTime($membershipEndDate, "+1 $interval_unit");
2355 $afterFirstUnit = $this->createModifiedDateTime($membershipEndDate, "+2 $interval_unit");
9099cab3
CW
2356 $cronRuns = [
2357 [
52f09320 2358 'time' => $beforeEndDate->format('Y-m-d H:i:s'),
9099cab3
CW
2359 'recipients' => [],
2360 ],
2361 [
52f09320 2362 'time' => $membershipEndDate->format('Y-m-d H:i:s'),
56ed359c 2363 'recipients' => [["test-member-$interval_unit@example.com"]],
9099cab3
CW
2364 ],
2365 [
52f09320 2366 'time' => $beforeFirstUnit->format('Y-m-d H:i:s'),
9099cab3
CW
2367 'recipients' => [],
2368 ],
2369 [
52f09320 2370 'time' => $afterFirstUnit->format('Y-m-d H:i:s'),
56ed359c 2371 'recipients' => [["test-member-$interval_unit@example.com"]],
9099cab3
CW
2372 ],
2373 ];
52f09320
PH
2374 $this->assertCronRuns($cronRuns);
2375 $actionSchedule->delete();
2376 $membership->delete();
2377 }
2378 }
2379
bb3ba83e
AH
2380 /**
2381 * Inherited members without permission to edit the main member contact should
2382 * not get reminders.
2383 *
2384 * However, just because a contact inherits one membership doesn't mean
2385 * reminders for other memberships should be suppressed.
2386 *
2387 * See CRM-14098
34662dda 2388 *
2389 * @throws \CRM_Core_Exception
bb3ba83e 2390 */
5bcf6842 2391 public function testInheritedMembershipPermissions(): void {
bb3ba83e
AH
2392 // Set up common parameters for memberships.
2393 $membershipParams = $this->fixtures['rolling_membership'];
2394 $membershipParams['status_id'] = 1;
2395
2396 $membershipParams['membership_type_id']['relationship_type_id'] = 1;
2397 $membershipParams['membership_type_id']['relationship_direction'] = 'b_a';
2398 $membershipType1 = $this->createTestObject('CRM_Member_DAO_MembershipType', $membershipParams['membership_type_id']);
2399
2400 // We'll create a new membership type that can be held at the same time as
2401 // the first one.
2402 $membershipParams['membership_type_id']['relationship_type_id'] = 'NULL';
2403 $membershipParams['membership_type_id']['relationship_direction'] = 'NULL';
2404 $membershipType2 = $this->createTestObject('CRM_Member_DAO_MembershipType', $membershipParams['membership_type_id']);
2405
2406 // Create the parent membership and contact
2407 $membershipParams['membership_type_id'] = $membershipType1->id;
2408 $mainMembership = $this->createTestObject('CRM_Member_DAO_Membership', $membershipParams);
2409
2410 $contactParams = [
2411 'contact_type' => 'Individual',
2412 'first_name' => 'Mom',
2413 'last_name' => 'Rel',
2414 'is_deceased' => 0,
2415 ];
2416 $this->createTestObject('CRM_Contact_DAO_Contact', array_merge($contactParams, ['id' => $mainMembership->contact_id]));
2417
2418 $emailParams = [
2419 'contact_id' => $mainMembership->contact_id,
2420 'email' => 'test-member@example.com',
2421 'location_type_id' => 1,
2422 'is_primary' => 1,
2423 ];
34662dda 2424 $this->createTestObject('CRM_Core_DAO_Email', $emailParams);
bb3ba83e
AH
2425
2426 // Set up contacts and emails for the two children
2427 $contactParams['first_name'] = 'Favorite';
2428 $permChild = $this->createTestObject('CRM_Contact_DAO_Contact', $contactParams);
2429 $emailParams['email'] = 'favorite@example.com';
2430 $emailParams['contact_id'] = $permChild->id;
2431 $this->createTestObject('CRM_Core_DAO_Email', $emailParams);
2432
2433 $contactParams['first_name'] = 'Black Sheep';
2434 $nonPermChild = $this->createTestObject('CRM_Contact_DAO_Contact', $contactParams);
2435 $emailParams['email'] = 'black.sheep@example.com';
2436 $emailParams['contact_id'] = $nonPermChild->id;
2437 $this->createTestObject('CRM_Core_DAO_Email', $emailParams);
2438
2439 // Each child gets a relationship, one with permission to edit the parent. This
2440 // will trigger inherited memberships for the first membership type
2441 $relParams = [
2442 'relationship_type_id' => 1,
2443 'contact_id_a' => $nonPermChild->id,
2444 'contact_id_b' => $mainMembership->contact_id,
2445 'is_active' => 1,
2446 ];
2447 $this->callAPISuccess('relationship', 'create', $relParams);
2448
2449 $relParams['contact_id_a'] = $permChild->id;
2450 $relParams['is_permission_a_b'] = CRM_Contact_BAO_Relationship::EDIT;
2451 $this->callAPISuccess('relationship', 'create', $relParams);
2452
2453 // Mom and Black Sheep get their own memberships of the second type.
2454 $membershipParams['membership_type_id'] = $membershipType2->id;
2455 $membershipParams['owner_membership_id'] = 'NULL';
2456 $membershipParams['contact_id'] = $mainMembership->contact_id;
2457 $this->createTestObject('CRM_Member_DAO_Membership', $membershipParams);
2458
2459 $membershipParams['contact_id'] = $nonPermChild->id;
2460 $this->createTestObject('CRM_Member_DAO_Membership', $membershipParams);
2461
2462 // Test a reminder for the first membership type - that should exclude Black
2463 // Sheep.
c5e1bf98 2464 $this->fixtures['sched_membership_join_2week']['entity_value'] = $membershipType1->id;
2465 $this->createScheduleFromFixtures('sched_membership_join_2week');
bb3ba83e
AH
2466
2467 $this->assertCronRuns([
2468 [
2469 'time' => '2012-03-29 01:00:00',
2470 'recipients' => [['test-member@example.com'], ['favorite@example.com']],
2471 'subjects' => [
2472 'subject sched_membership_join_2week (joined March 15th, 2012)',
2473 'subject sched_membership_join_2week (joined March 15th, 2012)',
2474 ],
2475 ],
2476 ]);
2477
2478 // Test a reminder for the second membership type - that should include
2479 // Black Sheep.
c5e1bf98 2480 $this->fixtures['sched_membership_start_1week']['entity_value'] = $membershipType2->id;
2481 $this->createScheduleFromFixtures('sched_membership_start_1week');
bb3ba83e
AH
2482
2483 $this->assertCronRuns([
2484 [
2485 'time' => '2012-03-22 01:00:00',
2486 'recipients' => [['test-member@example.com'], ['black.sheep@example.com']],
2487 'subjects' => [
2488 'subject sched_membership_start_1week (joined March 15th, 2012)',
2489 'subject sched_membership_start_1week (joined March 15th, 2012)',
2490 ],
2491 ],
2492 ]);
2493 }
2494
34662dda 2495 /**
2496 * Modify the date time by the modify rule.
2497 *
2498 * @param DateTime $origDateTime
2499 * @param string $modifyRule
2500 *
2501 * @return DateTime
2502 */
c5e1bf98 2503 public function createModifiedDateTime(DateTime $origDateTime, string $modifyRule): DateTime {
52f09320
PH
2504 $newDateTime = clone($origDateTime);
2505 $newDateTime->modify($modifyRule);
2506 return $newDateTime;
2507 }
2508
34662dda 2509 /**
2510 * Test absolute date handling for membership.
2511 *
5bcf6842 2512 * @throws \API_Exception
34662dda 2513 * @throws \CRM_Core_Exception
56ed359c
EM
2514 * @throws \CiviCRM_API3_Exception
2515 * @throws \Civi\API\Exception\UnauthorizedException
34662dda 2516 */
5bcf6842 2517 public function testMembershipScheduleWithAbsoluteDate(): void {
34662dda 2518 $membership = $this->createMembershipFromFixture('rolling_membership', 'New', [
e08fae02
PH
2519 'email' => 'test-member@example.com',
2520 'location_type_id' => 1,
9099cab3 2521 ]);
e08fae02 2522
56ed359c
EM
2523 $this->callAPISuccess('contact', 'create', array_merge($this->fixtures['contact'], ['contact_id' => $membership['contact_id']]));
2524 $this->fixtures['sched_membership_absolute_date']['entity_value'] = $membership['membership_type_id'];
c5e1bf98 2525 $this->createScheduleFromFixtures('sched_membership_absolute_date');
e08fae02 2526
9099cab3
CW
2527 $this->assertCronRuns([
2528 [
e08fae02
PH
2529 // Before the 24-hour mark, no email
2530 'time' => '2012-06-13 04:00:00',
9099cab3
CW
2531 'recipients' => [],
2532 'subjects' => [],
2533 ],
2534 [
e08fae02
PH
2535 // On absolute date set on 2012-06-14
2536 'time' => '2012-06-14 00:00:00',
9099cab3
CW
2537 'recipients' => [['test-member@example.com']],
2538 'subjects' => ['subject sched_membership_absolute_date'],
2539 ],
2540 [
e08fae02
PH
2541 // Run cron 4 hours later; first message already sent
2542 'time' => '2012-06-14 04:00:00',
9099cab3
CW
2543 'recipients' => [],
2544 'subjects' => [],
2545 ],
2546 ]);
e08fae02
PH
2547 }
2548
34662dda 2549 /**
2550 * @param string $fixture
2551 * Key from $this->fixtures
2552 * @param string $status
2553 * Membership status
2554 * @param array $emailParams
56ed359c 2555 * @param array $membershipOverrides
34662dda 2556 *
56ed359c 2557 * @return array
34662dda 2558 * @throws \API_Exception
56ed359c
EM
2559 * @throws \CRM_Core_Exception
2560 * @throws \CiviCRM_API3_Exception
2561 * @throws \Civi\API\Exception\UnauthorizedException
34662dda 2562 */
56ed359c
EM
2563 protected function createMembershipFromFixture(string $fixture, string $status, array $emailParams = [], array $membershipOverrides = []): array {
2564 $membershipTypeID = $membershipOverrides['membership_type_id'] ?? $this->fixtures[$fixture]['membership_type_id'];
2565 if (is_array($membershipTypeID)) {
2566 $membershipTypeID = MembershipType::create()->setValues(array_merge([
2567 'member_of_contact_id' => 1,
2568 'financial_type_id:name' => 'Member Dues',
2569 'name' => 'fixture-created-type',
2570 ], $this->fixtures[$fixture]['membership_type_id']))->execute()->first()['id'];
2571 }
2572 $params = array_merge($this->fixtures[$fixture], [
2573 'sequential' => 1,
2574 'status_id' => CRM_Core_PseudoConstant::getKey('CRM_Member_BAO_Membership', 'status_id', $status),
2575 'membership_type_id' => $membershipTypeID,
2576 ], $membershipOverrides);
2577 if (empty($params['contact_id'])) {
2578 $params['contact_id'] = $this->individualCreate(['email' => '']);
2579 }
2580 $membership = (array) $this->callAPISuccess('Membership', 'create', $params)['values'][0];
34662dda 2581 if ($emailParams) {
fe806431 2582 Civi\Api4\Email::create(FALSE)->setValues(array_merge([
56ed359c 2583 'contact_id' => $membership['contact_id'],
34662dda 2584 'location_type_id' => 1,
2585 ], $emailParams))->execute();
2586 }
2587 return $membership;
2588 }
2589
2590 /**
2591 * Create action schedule from defined fixtures.
2592 *
2593 * @param string $fixture
c5e1bf98 2594 * @param array $extraParams
2595 *
2596 * @throws \CRM_Core_Exception
34662dda 2597 */
56ed359c 2598 protected function createScheduleFromFixtures(string $fixture, array $extraParams = []): void {
c5e1bf98 2599 $id = $this->callAPISuccess('ActionSchedule', 'create', array_merge($this->fixtures[$fixture], $extraParams))['id'];
2600 $this->fixtures[$fixture]['action_schedule_id'] = (int) $id;
34662dda 2601 }
2602
e8113103 2603 /**
2604 * @param string $activityKey
2605 * @param string $contactKey
2606 *
2607 * @throws \CRM_Core_Exception
2608 */
2609 protected function createActivityAndContactFromFixtures(string $activityKey = 'phone_call', string $contactKey = 'contact'): void {
2610 $activity = $this->createTestObject('CRM_Activity_DAO_Activity', $this->fixtures[$activityKey]);
2611 $contact = $this->callAPISuccess('contact', 'create', $this->fixtures[$contactKey]);
2612 $activity->save();
2613
2614 $source = [];
2615 $source['contact_id'] = $contact['id'];
2616 $source['activity_id'] = $activity->id;
2617 $source['record_type_id'] = 2;
2618 $activityContact = $this->createTestObject('CRM_Activity_DAO_ActivityContact', $source);
2619 $activityContact->save();
2620 }
2621
6a488035 2622}