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