Merge pull request #20802 from civicrm/5.39
[civicrm-core.git] / tests / phpunit / CRM / Core / BAO / RecurringEntityTest.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
5 | |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
10 */
11
12 /**
13 * Class CRM_Core_BAO_RecurringEntityTest
14 * @group headless
15 */
16 class CRM_Core_BAO_RecurringEntityTest extends CiviUnitTestCase {
17
18 /**
19 * Testing Activity Generation through Entity Recursion.
20 */
21 public function testActivityGeneration() {
22 //Activity set initial params
23 $daoActivity = new CRM_Activity_DAO_Activity();
24 $daoActivity->activity_type_id = 1;
25 $daoActivity->subject = "Initial Activity";
26 $daoActivity->activity_date_time = '20141002103000';
27 $daoActivity->save();
28
29 $recursion = new CRM_Core_BAO_RecurringEntity();
30 $recursion->entity_id = $daoActivity->id;
31 $recursion->entity_table = 'civicrm_activity';
32 $recursion->dateColumns = ['activity_date_time'];
33 $recursion->schedule = [
34 'entity_value' => $daoActivity->id,
35 'start_action_date' => $daoActivity->activity_date_time,
36 'entity_status' => 'fourth saturday',
37 'repetition_frequency_unit' => 'month',
38 'repetition_frequency_interval' => 3,
39 'start_action_offset' => 5,
40 'used_for' => 'activity',
41 ];
42
43 $generatedEntities = $recursion->generate();
44 $this->assertEquals(5, count($generatedEntities['civicrm_activity']), "Cehck if number of iterations are 5");
45 $expectedDates = [
46 '20141025103000',
47 '20150124103000',
48 '20150425103000',
49 '20150725103000',
50 '20151024103000',
51 ];
52 foreach ($generatedEntities['civicrm_activity'] as $entityID) {
53 $this->assertDBNotNull('CRM_Activity_DAO_Activity', $entityID, 'id',
54 'id', 'Check DB if repeating activities were created'
55 );
56 }
57
58 // set mode to ALL, i.e any change to changing activity affects all related recurring activities
59 $recursion->mode(3);
60
61 // lets change subject of initial activity that we created in beginning
62 $daoActivity->find(TRUE);
63 $daoActivity->subject = 'Changed Activity';
64 $daoActivity->save();
65
66 // check if other activities were affected
67 $actualDates = [];
68 foreach ($generatedEntities['civicrm_activity'] as $entityID) {
69 $this->assertDBCompareValue('CRM_Activity_DAO_Activity', $entityID, 'subject', 'id', 'Changed Activity', 'Check if subject was updated');
70 $actualDates[] = date('YmdHis', strtotime(CRM_Core_DAO::getFieldValue('CRM_Activity_DAO_Activity', $entityID, 'activity_date_time', 'id')));
71 }
72 $resultDates = array_diff($actualDates, $expectedDates);
73 $this->assertEquals(0, count($resultDates), "Check if all the value in expected array matches actual array");
74
75 }
76
77 /**
78 * Creating action schedule
79 */
80 private function createActionSchedule($entity_id, $entity_table) {
81 $params = [
82 "used_for" => $entity_table,
83 "entity_value" => $entity_id,
84 "start_action_date" => date("YmdHis"),
85 "repetition_frequency_unit" => "week",
86 "repetition_frequency_interval" => "3",
87 "start_action_condition" => "monday,tuesday,wednesday,thursday,friday,saturday",
88 "start_action_offset" => "2",
89 ];
90 $actionScheduleObj = CRM_Core_BAO_ActionSchedule::add($params);
91 return $actionScheduleObj;
92 }
93
94 /**
95 * Creating recurring entities
96 */
97 private function createRecurringEntities($actionScheduleObj, $entity_id, $entity_table) {
98 $recursion = new CRM_Core_BAO_RecurringEntity();
99 $recursion->dateColumns = [
100 "start_date",
101 ];
102 $recursion->scheduleId = $actionScheduleObj->id;
103 $recursion->entity_id = $entity_id;
104 $recursion->entity_table = $entity_table;
105 $recursion->linkedEntities = [
106 [
107 "table" => "civicrm_price_set_entity",
108 "findCriteria" => [
109 "entity_id" => $entity_id,
110 "entity_table" => $entity_table,
111 ],
112 "linkedColumns" => [
113 "entity_id",
114 ],
115 "isRecurringEntityRecord" => FALSE,
116 ],
117 ];
118 return $recursion->generate();
119 }
120
121 /**
122 * Testing Event Generation through Entity Recursion.
123 */
124 public function testRepeatEventCreation() {
125 $event = $this->eventCreate();
126 $entity_table = "civicrm_event";
127 $entity_id = $event["id"];
128 CRM_Price_BAO_PriceSet::addTo($entity_table, $entity_id, 1);
129 $actionScheduleObj = $this->createActionSchedule($entity_id, $entity_table);
130 $recurringEntities = $this->createRecurringEntities($actionScheduleObj, $entity_id, $entity_table);
131 $finalResult = CRM_Core_BAO_RecurringEntity::updateModeAndPriceSet($entity_id, $entity_table, CRM_Core_BAO_RecurringEntity::MODE_ALL_ENTITY_IN_SERIES, [], 2);
132 $this->assertEquals(2, count($recurringEntities["civicrm_event"]), "Recurring events not created.");
133 $this->assertEquals(2, count($recurringEntities["civicrm_price_set_entity"]), "Recurring price sets not created.");
134 $priceSetOne = CRM_Price_BAO_PriceSet::getFor($entity_table, $recurringEntities["civicrm_price_set_entity"][0]);
135 $priceSetTwo = CRM_Price_BAO_PriceSet::getFor($entity_table, $recurringEntities["civicrm_price_set_entity"][1]);
136 $this->assertEquals(2, $priceSetOne, "Price set id of the recurring event is not updated.");
137 $this->assertEquals(2, $priceSetTwo, "Price set id of the recurring event is not updated.");
138 }
139
140 /**
141 * Testing Event Generation through Entity Recursion.
142 */
143 public function testEventGeneration() {
144 //Event set initial params
145 $daoEvent = new CRM_Event_DAO_Event();
146 $daoEvent->title = 'Test event for Recurring Entity';
147 $daoEvent->event_type_id = 3;
148 $daoEvent->is_public = 1;
149 $daoEvent->start_date = date('YmdHis', strtotime('2014-10-26 10:30:00'));
150 $daoEvent->end_date = date('YmdHis', strtotime('2014-10-28 10:30:00'));
151 $daoEvent->created_date = date('YmdHis');
152 $daoEvent->is_active = 1;
153 $daoEvent->save();
154 $this->assertDBNotNull('CRM_Event_DAO_Event', $daoEvent->id, 'id', 'id', 'Check DB if event was created');
155
156 //Create tell a friend for event
157 $daoTellAFriend = new CRM_Friend_DAO_Friend();
158 $daoTellAFriend->entity_table = 'civicrm_event';
159 // join with event
160 $daoTellAFriend->entity_id = $daoEvent->id;
161 $daoTellAFriend->title = 'Testing tell a friend';
162 $daoTellAFriend->is_active = 1;
163 $daoTellAFriend->save();
164 $this->assertDBNotNull('CRM_Friend_DAO_Friend', $daoTellAFriend->id, 'id', 'id', 'Check DB if tell a friend was created');
165
166 // time to use recursion
167 $recursion = new CRM_Core_BAO_RecurringEntity();
168 $recursion->entity_id = $daoEvent->id;
169 $recursion->entity_table = 'civicrm_event';
170 $recursion->dateColumns = ['start_date'];
171 $recursion->schedule = [
172 'entity_value' => $daoEvent->id,
173 'start_action_date' => $daoEvent->start_date,
174 'start_action_condition' => 'monday',
175 'repetition_frequency_unit' => 'week',
176 'repetition_frequency_interval' => 1,
177 'start_action_offset' => 4,
178 'used_for' => 'event',
179 ];
180
181 $recursion->linkedEntities = [
182 [
183 'table' => 'civicrm_tell_friend',
184 'findCriteria' => [
185 'entity_id' => $recursion->entity_id,
186 'entity_table' => 'civicrm_event',
187 ],
188 'linkedColumns' => ['entity_id'],
189 'isRecurringEntityRecord' => TRUE,
190 ],
191 ];
192
193 $interval = $recursion->getInterval($daoEvent->start_date, $daoEvent->end_date);
194 $recursion->intervalDateColumns = ['end_date' => $interval];
195 $generatedEntities = $recursion->generate();
196 $this->assertArrayHasKey('civicrm_event', $generatedEntities, 'Check if generatedEntities has civicrm_event as required key');
197 $expectedDates = [
198 '20141027103000' => '20141029103000',
199 '20141103103000' => '20141105103000',
200 '20141110103000' => '20141112103000',
201 '20141117103000' => '20141119103000',
202 ];
203
204 $this->assertCount($recursion->schedule['start_action_offset'], $generatedEntities['civicrm_event'], 'Check if the number of events created are right');
205 $actualDates = [];
206 foreach ($generatedEntities['civicrm_event'] as $key => $val) {
207 $this->assertDBNotNull('CRM_Event_DAO_Event', $val, 'id', 'id', 'Check if repeating events were created.');
208 $startDate = date('YmdHis', strtotime(CRM_Core_DAO::getFieldValue('CRM_Event_DAO_Event', $val, 'start_date', 'id')));
209 $endDate = date('YmdHis', strtotime(CRM_Core_DAO::getFieldValue('CRM_Event_DAO_Event', $val, 'end_date', 'id')));
210 $actualDates[$startDate] = $endDate;
211 }
212
213 $resultDates = array_diff($actualDates, $expectedDates);
214 $this->assertEquals(0, count($resultDates), "Check if all the value in expected array matches actual array");
215
216 foreach ($generatedEntities['civicrm_tell_friend'] as $key => $val) {
217 $this->assertDBNotNull('CRM_Friend_DAO_Friend', $val, 'id', 'id', 'Check if friends were created in loop');
218 $this->assertDBCompareValue('CRM_Friend_DAO_Friend', $val, 'entity_id', 'id', $generatedEntities['civicrm_event'][$key], 'Check DB if correct FK was maintained with event for Friend');
219 }
220 $this->assertCount($recursion->schedule['start_action_offset'], $generatedEntities['civicrm_tell_friend'], 'Check if the number of tell a friend records are right');
221
222 // set mode to ALL, i.e any change to changing event affects all related recurring activities
223 $recursion->mode(3);
224
225 $daoEvent->find(TRUE);
226 $daoEvent->title = 'Event Changed';
227 $daoEvent->save();
228
229 // check if other events were affected
230 foreach ($generatedEntities['civicrm_event'] as $entityID) {
231 $this->assertDBCompareValue('CRM_Event_DAO_Event', $entityID, 'title', 'id', 'Event Changed', 'Check if title was updated');
232 }
233
234 end($generatedEntities['civicrm_event']);
235 $key = key($generatedEntities['civicrm_event']);
236
237 end($generatedEntities['civicrm_tell_friend']);
238 $actKey = key($generatedEntities['civicrm_tell_friend']);
239
240 //Check if both(event/tell a friend) keys are same
241 $this->assertEquals($key, $actKey, "Check if both the keys are same");
242
243 //Cross check event exists before we test deletion
244 $searchParamsEventBeforeDelete = [
245 'entity_id' => $generatedEntities['civicrm_event'][$key],
246 'entity_table' => 'civicrm_event',
247 ];
248 $expectedValuesEventBeforeDelete = [
249 'entity_id' => $generatedEntities['civicrm_event'][$key],
250 'entity_table' => 'civicrm_event',
251 ];
252 $this->assertDBCompareValues('CRM_Core_DAO_RecurringEntity', $searchParamsEventBeforeDelete, $expectedValuesEventBeforeDelete);
253
254 //Cross check event exists before we test deletion
255 $searchParamsTellAFriendBeforeDelete = [
256 'entity_id' => $generatedEntities['civicrm_tell_friend'][$actKey],
257 'entity_table' => 'civicrm_tell_friend',
258 ];
259 $expectedValuesTellAFriendBeforeDelete = [
260 'entity_id' => $generatedEntities['civicrm_tell_friend'][$actKey],
261 'entity_table' => 'civicrm_tell_friend',
262 ];
263 $this->assertDBCompareValues('CRM_Core_DAO_RecurringEntity', $searchParamsTellAFriendBeforeDelete, $expectedValuesTellAFriendBeforeDelete);
264
265 //Delete an event from recurring set and respective linked entity should be deleted from civicrm_recurring_entity_table
266 $daoRecurEvent = new CRM_Event_DAO_Event();
267 $daoRecurEvent->id = $generatedEntities['civicrm_event'][$key];
268 if ($daoRecurEvent->find(TRUE)) {
269 $daoRecurEvent->delete();
270 }
271
272 //Check if this event_id was deleted
273 $this->assertDBNull('CRM_Event_DAO_Event', $generatedEntities['civicrm_event'][$key], 'id', 'id', 'Check if event was deleted');
274 $searchParams = [
275 'entity_id' => $generatedEntities['civicrm_event'][$key],
276 'entity_table' => 'civicrm_event',
277 ];
278 $compareParams = [];
279 $this->assertDBCompareValues('CRM_Core_DAO_RecurringEntity', $searchParams, $compareParams);
280
281 //Find tell_a_friend id if that was deleted from civicrm
282 $searchActParams = [
283 'entity_id' => $generatedEntities['civicrm_tell_friend'][$actKey],
284 'entity_table' => 'civicrm_tell_friend',
285 ];
286 $compareActParams = [];
287 $this->assertDBCompareValues('CRM_Friend_DAO_Friend', $searchActParams, $compareActParams);
288 }
289
290 /**
291 * Testing Activity Generation through Entity Recursion with Custom Data and Tags.
292 */
293 public function testRecurringEntityGenerationWithCustomDataAndTags() {
294
295 // Create custom group and field
296 $customGroup = $this->customGroupCreate([
297 'extends' => 'Activity',
298 ]);
299 $customField = $this->customFieldCreate([
300 'custom_group_id' => $customGroup['id'],
301 'default_value' => '',
302 ]);
303
304 // Create activity Tag
305 $tag = $this->tagCreate([
306 'used_for' => 'Activities',
307 ]);
308
309 // Create original activity
310 $customFieldValue = 'Custom Value';
311 $activityDateTime = date('YmdHis');
312 $activityId = $this->activityCreate([
313 'activity_date_time' => $activityDateTime,
314 'custom_' . $customField['id'] => $customFieldValue,
315 ]);
316
317 $activityId = $activityId['id'];
318
319 // Assign tag to a activity.
320 $this->callAPISuccess('EntityTag', 'create', [
321 'entity_table' => 'civicrm_activity',
322 'entity_id' => $activityId,
323 'tag_id' => $tag['id'],
324 ]);
325
326 // Create recurring activities.
327 $recursion = new CRM_Core_BAO_RecurringEntity();
328 $recursion->entity_id = $activityId;
329 $recursion->entity_table = 'civicrm_activity';
330 $recursion->dateColumns = ['activity_date_time'];
331 $recursion->schedule = [
332 'entity_value' => $activityId,
333 'start_action_date' => $activityDateTime,
334 'entity_status' => 'fourth saturday',
335 'repetition_frequency_unit' => 'month',
336 'repetition_frequency_interval' => 3,
337 'start_action_offset' => 3,
338 'used_for' => 'activity',
339 ];
340
341 $generatedEntities = $recursion->generate();
342 $generatedActivities = $generatedEntities['civicrm_activity'];
343
344 $this->assertEquals(3, count($generatedActivities), "Check if number of iterations are 3");
345
346 foreach ($generatedActivities as $generatedActivityId) {
347
348 /* Validate tag in recurring activity
349 // @todo - refer https://github.com/civicrm/civicrm-core/pull/13470
350 $this->callAPISuccess('EntityTag', 'getsingle', [
351 'entity_table' => 'civicrm_activity',
352 'entity_id' => $generatedActivityId,
353 ]);
354 */
355
356 // Validate custom data in recurring activity
357 $activity = $this->callAPISuccess('activity', 'getsingle', [
358 'return' => [
359 'custom_' . $customField['id'],
360 ],
361 'id' => $generatedActivityId,
362 ]);
363
364 $this->assertEquals($customFieldValue, $activity['custom_' . $customField['id']], 'Custom field value should be ' . $customFieldValue);
365
366 }
367 }
368
369 }