Merge pull request #15584 from civicrm/5.19
[civicrm-core.git] / tests / phpunit / api / v3 / PledgeTest.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 5 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2019 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
13 | |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
26 */
27
28 /**
29 * Test class for Pledge API - civicrm_pledge_*
30 *
31 * @package CiviCRM_APIv3
32 * @group headless
33 */
34 class api_v3_PledgeTest extends CiviUnitTestCase {
35
36 protected $_individualId;
37 protected $_pledge;
38 protected $_params;
39 protected $_entity;
40 protected $scheduled_date;
41
42 /**
43 * @throws \CRM_Core_Exception
44 */
45 public function setUp() {
46 $this->_apiversion = 3;
47 parent::setUp();
48 $this->quickCleanup(['civicrm_pledge', 'civicrm_pledge_payment']);
49 //need to set scheduled payment in advance we are running test @ midnight & it becomes unexpectedly overdue
50 //due to timezone issues
51 $this->scheduled_date = date('Ymd', mktime(0, 0, 0, date("m"), date("d") + 2, date("y")));
52 $this->_entity = 'Pledge';
53 $this->_individualId = $this->individualCreate();
54 $this->_params = [
55 'contact_id' => $this->_individualId,
56 'pledge_create_date' => date('Ymd'),
57 'start_date' => date('Ymd'),
58 'scheduled_date' => $this->scheduled_date,
59 'amount' => 100.00,
60 'pledge_status_id' => '2',
61 'pledge_financial_type_id' => '1',
62 'pledge_original_installment_amount' => 20,
63 'frequency_interval' => 5,
64 'frequency_unit' => 'year',
65 'frequency_day' => 15,
66 'installments' => 5,
67 'sequential' => 1,
68 ];
69 }
70
71 public function tearDown() {
72 $this->contactDelete($this->_individualId);
73 }
74
75 /**
76 * Check with complete array + custom field.
77 *
78 * Note that the test is written on purpose without any
79 * variables specific to participant so it can be replicated into other entities
80 * and / or moved to the automated test suite
81 */
82 public function testCreateWithCustom() {
83 $ids = $this->entityCustomGroupWithSingleFieldCreate(__FUNCTION__, __FILE__);
84
85 $params = $this->_params;
86 $params['custom_' . $ids['custom_field_id']] = "custom string";
87
88 $result = $this->callAPISuccess($this->_entity, 'create', $params);
89 $this->assertAPISuccess($result, " testCreateWithCustom ");
90 $this->assertAPISuccess($result);
91 $getParams = ['id' => $result['id'], 'return.custom_' . $ids['custom_field_id'] => 1];
92 $check = $this->callAPISuccess($this->_entity, 'get', $getParams);
93 $this->callAPISuccess('pledge', 'delete', ['id' => $check['id']]);
94 $this->assertEquals("custom string", $check['values'][$check['id']]['custom_' . $ids['custom_field_id']]);
95
96 $this->customFieldDelete($ids['custom_field_id']);
97 $this->customGroupDelete($ids['custom_group_id']);
98 }
99
100 /**
101 * Test getfields function for pledge.
102 */
103 public function testGetfieldsPledge() {
104 $result = $this->callAPISuccess('pledge', 'getfields', ['action' => 'get']);
105 $this->assertEquals(1, $result['values']['next_pay_date']['api.return']);
106 }
107
108 /**
109 * Test get pledge api.
110 */
111 public function testGetPledge() {
112
113 $this->_pledge = $this->callAPISuccess('pledge', 'create', $this->_params);
114 $params = [
115 'pledge_id' => $this->_pledge['id'],
116 ];
117 $result = $this->callAPIAndDocument('pledge', 'get', $params, __FUNCTION__, __FILE__);
118 $pledge = $result['values'][$this->_pledge['id']];
119 $this->assertEquals($this->_individualId, $pledge['contact_id']);
120 $this->assertEquals($this->_pledge['id'], $pledge['pledge_id']);
121 $this->assertEquals(date('Y-m-d') . ' 00:00:00', $pledge['pledge_create_date']);
122 $this->assertEquals(100.00, $pledge['pledge_amount']);
123 $this->assertEquals('Pending Label**', $pledge['pledge_status']);
124 $this->assertEquals(5, $pledge['pledge_frequency_interval']);
125 $this->assertEquals('year', $pledge['pledge_frequency_unit']);
126 $this->assertEquals(date('Y-m-d', strtotime($this->scheduled_date)) . ' 00:00:00', $pledge['pledge_next_pay_date']);
127 $this->assertEquals($pledge['pledge_next_pay_amount'], 20.00);
128
129 $params2 = [
130 'pledge_id' => $this->_pledge['id'],
131 ];
132 $pledge = $this->callAPISuccess('pledge', 'delete', $params2);
133 }
134
135 /**
136 * Test 'return.pledge_financial_type' => 1 works.
137 */
138 public function testGetPledgeWithReturn() {
139
140 $this->_pledge = $this->callAPISuccess('pledge', 'create', $this->_params);
141 $params = [
142 'pledge_id' => $this->_pledge['id'],
143 'return.pledge_financial_type' => 1,
144 ];
145 $result = $this->callAPISuccess('pledge', 'get', $params);
146 $pledge = $result['values'][$this->_pledge['id']];
147 $this->callAPISuccess('pledge', 'delete', $pledge);
148 $this->assertEquals('Donation', $pledge['pledge_financial_type']);
149 }
150
151 /**
152 * Test 'return.pledge_contribution_type' => 1 works.
153 *
154 * This is for legacy compatibility
155 */
156 public function testGetPledgeWithReturnLegacy() {
157
158 $this->_pledge = $this->callAPISuccess('pledge', 'create', $this->_params);
159 $params = [
160 'pledge_id' => $this->_pledge['id'],
161 'return.pledge_financial_type' => 1,
162 ];
163 $result = $this->callAPISuccess('pledge', 'get', $params);
164 $pledge = $result['values'][$this->_pledge['id']];
165 $this->callAPISuccess('pledge', 'delete', $pledge);
166 $this->assertEquals('Donation', $pledge['pledge_financial_type']);
167 }
168
169 /**
170 * Test date legacy date filters like pledge_start_date_high.
171 */
172 public function testPledgeGetReturnFilters() {
173 $this->callAPISuccess('pledge', 'create', $this->_params);
174
175 $overdueParams = [
176 'scheduled_date' => 'first saturday of march last year',
177 'start_date' => 'first saturday of march last year',
178 ];
179 $oldPledge = $this->callAPISuccess('pledge', 'create', array_merge($this->_params, $overdueParams));
180
181 $pledgeGetParams = [];
182 $allPledges = $this->callAPISuccess('pledge', 'getcount', $pledgeGetParams);
183
184 $this->assertEquals(2, $allPledges, 'Check we have 2 pledges to place with in line ' . __LINE__);
185 $pledgeGetParams['pledge_start_date_high'] = date('YmdHis', strtotime('2 days ago'));
186 $earlyPledge = $this->callAPIAndDocument('pledge', 'get', $pledgeGetParams, __FUNCTION__, __FILE__, "demonstrates high date filter", "GetFilterHighDate");
187 $this->assertEquals(1, $earlyPledge['count'], ' check only one returned with start date filter in line ' . __LINE__);
188 $this->assertEquals($oldPledge['id'], $earlyPledge['id'], ' check correct pledge returned ' . __LINE__);
189 }
190
191 /**
192 * Create 2 pledges - see if we can get by status id.
193 */
194 public function testGetOverduePledge() {
195 $overdueParams = [
196 'scheduled_date' => 'first saturday of march last year',
197 'start_date' => 'first saturday of march last year',
198 ];
199 $this->_pledge = $this->callAPISuccess('pledge', 'create', array_merge($this->_params, $overdueParams));
200
201 $result = $this->callAPISuccess('pledge', 'get', ['status_id' => 'Overdue']);
202 $emptyResult = $this->callAPISuccess('pledge', 'get', [
203 'pledge_status_id' => '1',
204 ]);
205 $pledge = $result['values'][$this->_pledge['id']];
206 $this->callAPISuccess('pledge', 'delete', $pledge);
207 $this->assertEquals(1, $result['count']);
208 $this->assertEquals(0, $emptyResult['count']);
209 }
210
211 /**
212 * Test pledge_status option group
213 */
214 public function testOptionGroupForPledgeStatus() {
215 $pledgeOg = $this->callAPISuccess('OptionGroup', 'get', [
216 'name' => "pledge_status",
217 ]);
218 $this->assertEquals(1, $pledgeOg['count']);
219
220 $pledgeOv = $this->callAPISuccess('OptionValue', 'get', [
221 'sequential' => 1,
222 'option_group_id' => "pledge_status",
223 ]);
224 $this->assertEquals(5, $pledgeOv['count']);
225 $pledgeStatus = CRM_Utils_Array::collect('name', $pledgeOv['values']);
226 $expected = ['Completed', 'Pending', 'Cancelled', 'In Progress', 'Overdue'];
227 $this->assertEquals($expected, $pledgeStatus);
228 }
229
230 /**
231 * Create 2 pledges - see if we can get by status id.
232 */
233 public function testSortParamPledge() {
234 $pledge1 = $this->callAPISuccess('pledge', 'create', $this->_params);
235 $overdueParams = [
236 'scheduled_date' => 'first saturday of march last year',
237 'start_date' => 'first saturday of march last year',
238 'create_date' => 'first saturday of march last year',
239 ];
240 $pledge2 = $this->callAPISuccess('pledge', 'create', array_merge($this->_params, $overdueParams));
241 $params = [
242 'pledge_is_test' => 0,
243 'rowCount' => 1,
244 ];
245 $result = $this->callAPISuccess('pledge', 'get', $params);
246
247 $resultSortedAsc = $this->callAPISuccess('pledge', 'get', [
248 'rowCount' => 1,
249 'sort' => 'start_date ASC',
250 ]);
251 $resultSortedDesc = $this->callAPISuccess('pledge', 'get', [
252 'rowCount' => 1,
253 'sort' => 'start_date DESC',
254 ]);
255
256 $this->assertEquals($pledge1['id'], $result['id'], 'pledge get gets first created pledge in line ' . __LINE__);
257 $this->assertEquals($pledge2['id'], $resultSortedAsc['id'], 'Ascending pledge sort works');
258 $this->assertEquals($pledge1['id'], $resultSortedDesc['id'], 'Decending pledge sort works');
259 $this->callAPISuccess('pledge', 'delete', ['id' => $pledge1['id']]);
260 $this->callAPISuccess('pledge', 'delete', ['id' => $pledge2['id']]);
261 }
262
263 public function testCreatePledge() {
264
265 $result = $this->callAPIAndDocument('pledge', 'create', $this->_params, __FUNCTION__, __FILE__);
266 $this->assertEquals($result['values'][0]['amount'], 100.00);
267 $this->assertEquals($result['values'][0]['installments'], 5);
268 $this->assertEquals($result['values'][0]['frequency_unit'], 'year');
269 $this->assertEquals($result['values'][0]['frequency_interval'], 5);
270 $this->assertEquals($result['values'][0]['frequency_day'], 15);
271 $this->assertEquals($result['values'][0]['original_installment_amount'], 20);
272 $this->assertEquals($result['values'][0]['status_id'], 2);
273 $this->assertEquals($result['values'][0]['create_date'], date('Ymd') . '000000');
274 $this->assertEquals($result['values'][0]['start_date'], date('Ymd') . '000000');
275 $this->assertAPISuccess($result);
276 $payments = $this->callAPISuccess('PledgePayment', 'Get', ['pledge_id' => $result['id'], 'sequential' => 1]);
277 $this->assertAPISuccess($payments);
278 $this->assertEquals($payments['count'], 5);
279 $shouldBeDate = CRM_Utils_Date::format(CRM_Utils_Date::intervalAdd('year', 5 * 4, $this->scheduled_date), "-");
280 $this->assertEquals(substr($shouldBeDate, 0, 10), substr($payments['values'][4]['scheduled_date'], 0, 10));
281
282 $pledgeID = ['id' => $result['id']];
283 $pledge = $this->callAPISuccess('pledge', 'delete', $pledgeID);
284 }
285
286 /**
287 * Test that pledge with weekly schedule calculates dates correctly.
288 */
289 public function testCreatePledgeWeeklySchedule() {
290 $params = [
291 'scheduled_date' => '20110510',
292 'frequency_unit' => 'week',
293 'frequency_day' => 3,
294 'frequency_interval' => 2,
295 ];
296 $params = array_merge($this->_params, $params);
297 $pledge = $this->callAPISuccess('Pledge', 'Create', $params);
298 //ensure that correct number of payments created & last payment has the right date
299 $payments = $this->callAPISuccess('PledgePayment', 'Get', [
300 'pledge_id' => $pledge['id'],
301 'sequential' => 1,
302 ]);
303 $this->assertEquals($payments['count'], 5);
304 $this->assertEquals('2011-07-06 00:00:00', $payments['values'][4]['scheduled_date']);
305
306 $this->callAPISuccess('pledge', 'delete', ['pledge_id' => $pledge['id']]);
307 }
308
309 /**
310 * Test that pledge with weekly schedule calculates dates correctly.
311 */
312 public function testCreatePledgeMontlySchedule() {
313 $params = [
314 'scheduled_date' => '20110510',
315 'frequency_unit' => 'Month',
316 'frequency_day' => 3,
317 'frequency_interval' => 2,
318 ];
319 $params = array_merge($this->_params, $params);
320 $apiResult = $this->callAPISuccess('pledge', 'create', $params);
321 }
322
323 /**
324 * Test creation of pledge with only one payment.
325 *
326 * Pledge status id left empty as it is not a required field
327 * http://issues.civicrm.org/jira/browse/CRM-8551
328 */
329 public function testCreatePledgeSinglePayment() {
330 $params = [
331 'scheduled_date' => '20110510',
332 'frequency_unit' => 'week',
333 'frequency_day' => 3,
334 'frequency_interval' => 2,
335 'installments' => 1,
336 ];
337
338 $params = array_merge($this->_params, $params);
339 unset($params['pledge_status_id']);
340 $pledge = $this->callAPISuccess('Pledge', 'Create', $params);
341 //ensure that correct number of payments created & last payment has the right date
342 $payments = $this->callAPISuccess('PledgePayment', 'Get', [
343 'pledge_id' => $pledge['id'],
344 'sequential' => 1,
345 ]);
346 $this->assertEquals(1, $payments['count']);
347 $this->assertEquals(2, $payments['values'][0]['status_id']);
348 $pledgeID = ['id' => $pledge['id']];
349 $pledge = $this->callAPISuccess('pledge', 'delete', $pledgeID);
350 }
351
352 /**
353 * Test that using original_installment_amount rather than pledge_original_installment_amount works.
354 *
355 * Pledge field behaviour is a bit random & so pledge has come to try to handle both unique & non -unique fields.
356 */
357 public function testCreatePledgeWithNonUnique() {
358 $params = $this->_params;
359 $params['original_installment_amount'] = $params['pledge_original_installment_amount'];
360
361 unset($params['pledge_original_installment_amount']);
362 $result = $this->callAPISuccess('pledge', 'create', $params);
363 $pledgeDetails = $this->callAPISuccess('Pledge', 'Get', ['id' => $result['id'], 'sequential' => 1]);
364 $pledge = $pledgeDetails['values'][0];
365 $this->assertEquals(100.00, $pledge['pledge_amount']);
366 $this->assertEquals('year', $pledge['pledge_frequency_unit']);
367 $this->assertEquals(5, $pledge['pledge_frequency_interval']);
368 $this->assertEquals(20, $pledge['pledge_next_pay_amount']);
369
370 $pledgeID = ['id' => $result['id']];
371 $pledge = $this->callAPISuccess('pledge', 'delete', $pledgeID);
372 }
373
374 /**
375 * Test cancelling a pledge.
376 */
377 public function testCreateCancelPledge() {
378
379 $result = $this->callAPISuccess('pledge', 'create', $this->_params);
380 $this->assertEquals(2, $result['values'][0]['status_id']);
381 $cancelParams = [
382 'sequential' => 1,
383 'id' => $result['id'],
384 'pledge_status_id' => 3,
385 ];
386 $result = $this->callAPISuccess('pledge', 'create', $cancelParams);
387 $this->assertEquals(3, $result['values'][0]['status_id']);
388 $pledgeID = ['id' => $result['id']];
389 $this->callAPISuccess('pledge', 'delete', $pledgeID);
390 }
391
392 /**
393 * Test that status is set to pending.
394 */
395 public function testCreatePledgeNoStatus() {
396
397 $params = $this->_params;
398 unset($params['status_id']);
399 unset($params['pledge_status_id']);
400 $result = $this->callAPISuccess('pledge', 'create', $params);
401 $this->assertAPISuccess($result);
402 $this->assertEquals(2, $result['values'][0]['status_id']);
403 $pledgeID = ['pledge_id' => $result['id']];
404 $pledge = $this->callAPISuccess('pledge', 'delete', $pledgeID);
405 }
406
407 /**
408 * Update Pledge.
409 */
410 public function testCreateUpdatePledge() {
411 // we test 'sequential' param here too
412 $pledgeID = $this->pledgeCreate(['contact_id' => $this->_individualId]);
413 $old_params = [
414 'id' => $pledgeID,
415 'sequential' => 1,
416 ];
417 $original = $this->callAPISuccess('pledge', 'get', $old_params);
418 //Make sure it came back
419 $this->assertEquals($original['values'][0]['pledge_id'], $pledgeID);
420 //set up list of old params, verify
421 $old_contact_id = $original['values'][0]['contact_id'];
422 $old_frequency_unit = $original['values'][0]['pledge_frequency_unit'];
423 $old_frequency_interval = $original['values'][0]['pledge_frequency_interval'];
424 $old_status_id = $original['values'][0]['pledge_status'];
425
426 //check against values in CiviUnitTestCase::createPledge()
427 $this->assertEquals($old_contact_id, $this->_individualId);
428 $this->assertEquals($old_frequency_unit, 'year');
429 $this->assertEquals($old_frequency_interval, 5);
430 $this->assertEquals($old_status_id, 'Pending Label**');
431 $params = [
432 'id' => $pledgeID,
433 'contact_id' => $this->_individualId,
434 'pledge_status_id' => 3,
435 'amount' => 100,
436 'financial_type_id' => 1,
437 'start_date' => date('Ymd'),
438 'installments' => 10,
439 ];
440
441 $pledge = $this->callAPISuccess('pledge', 'create', $params);
442 $new_params = [
443 'id' => $pledge['id'],
444 ];
445 $pledge = $this->callAPISuccess('pledge', 'get', $new_params);
446 $this->assertEquals($pledge['values'][$pledgeID]['contact_id'], $this->_individualId);
447 $this->assertEquals($pledge['values'][$pledgeID]['pledge_status'], 'Cancelled');
448 $pledge = $this->callAPISuccess('pledge', 'delete', $new_params);
449 }
450
451 /**
452 * Here we ensure we are maintaining our 'contract' & supporting previously working syntax.
453 *
454 * ie contribution_type_id.
455 *
456 * We test 'sequential' param here too.
457 */
458 public function testCreateUpdatePledgeLegacy() {
459 $pledgeID = $this->pledgeCreate(['contact_id' => $this->_individualId]);
460 $old_params = [
461 'id' => $pledgeID,
462 'sequential' => 1,
463 ];
464 $original = $this->callAPISuccess('pledge', 'get', $old_params);
465 // Make sure it came back.
466 $this->assertEquals($original['values'][0]['pledge_id'], $pledgeID);
467 // Set up list of old params, verify.
468 $old_contact_id = $original['values'][0]['contact_id'];
469 $old_frequency_unit = $original['values'][0]['pledge_frequency_unit'];
470 $old_frequency_interval = $original['values'][0]['pledge_frequency_interval'];
471 $old_status_id = $original['values'][0]['pledge_status'];
472
473 // Check against values in CiviUnitTestCase::createPledge().
474 $this->assertEquals($old_contact_id, $this->_individualId);
475 $this->assertEquals($old_frequency_unit, 'year');
476 $this->assertEquals($old_frequency_interval, 5);
477 $this->assertEquals($old_status_id, 'Pending Label**');
478 $params = [
479 'id' => $pledgeID,
480 'contact_id' => $this->_individualId,
481 'pledge_status_id' => 3,
482 'amount' => 100,
483 'contribution_type_id' => 1,
484 'start_date' => date('Ymd'),
485 'installments' => 10,
486 ];
487
488 $pledge = $this->callAPISuccess('pledge', 'create', $params);
489 $new_params = [
490 'id' => $pledge['id'],
491 ];
492 $pledge = $this->callAPISuccess('pledge', 'get', $new_params);
493 $this->assertEquals($pledge['values'][$pledgeID]['contact_id'], $this->_individualId);
494 $this->assertEquals($pledge['values'][$pledgeID]['pledge_status'], 'Cancelled');
495 $this->callAPISuccess('pledge', 'delete', $new_params);
496 }
497
498 /**
499 * Failure test for delete without id.
500 */
501 public function testDeleteEmptyParamsPledge() {
502 $this->callAPIFailure('pledge', 'delete', [], 'Mandatory key(s) missing from params array: id');
503 }
504
505 /**
506 * Failure test for invalid pledge id.
507 */
508 public function testDeleteWrongParamPledge() {
509 $params = [
510 'pledge_source' => 'SSF',
511 ];
512 $this->callAPIFailure('pledge', 'delete', $params, 'Mandatory key(s) missing from params array: id');
513 }
514
515 /**
516 * Legacy support for pledge_id.
517 */
518 public function testDeletePledge() {
519
520 $pledgeID = $this->pledgeCreate(['contact_id' => $this->_individualId]);
521 $params = [
522 'pledge_id' => $pledgeID,
523 ];
524 $this->callAPIAndDocument('pledge', 'delete', $params, __FUNCTION__, __FILE__);
525 }
526
527 /**
528 * Standard is to accept id.
529 */
530 public function testDeletePledgeUseID() {
531
532 $pledgeID = $this->pledgeCreate(['contact_id' => $this->_individualId]);
533 $params = [
534 'id' => $pledgeID,
535 ];
536 $this->callAPIAndDocument('pledge', 'delete', $params, __FUNCTION__, __FILE__);
537 }
538
539 /**
540 * Test to make sure empty get returns nothing.
541 *
542 * Note that the function gives incorrect results if no pledges exist as it does a
543 * contact search instead - test only checks that the get finds the one existing
544 */
545 public function testGetEmpty() {
546 $this->callAPISuccess('pledge', 'create', $this->_params);
547 $result = $this->callAPISuccess('pledge', 'get', []);
548 $this->assertAPISuccess($result, "This test is failing because it's acting like a contact get when no params set. Not sure the fix");
549 $this->assertEquals(1, $result['count']);
550 $pledgeID = ['id' => $result['id']];
551 $this->callAPISuccess('pledge', 'delete', $pledgeID);
552 }
553
554 }