4 * Class CRM_Event_BAO_AdditionalPaymentTest
8 class CRM_Event_BAO_ChangeFeeSelectionTest
extends CiviUnitTestCase
{
10 protected $_priceSetID;
12 protected $_cheapFee = 80;
14 protected $_expensiveFee = 100;
16 protected $_veryExpensive = 120;
18 protected $_noFee = 0;
20 protected $expensiveFeeValueID;
22 protected $cheapFeeValueID;
24 protected $veryExpensiveFeeValueID;
31 protected $contributionID;
36 protected $participantID;
43 protected $priceSetFieldID;
63 public function setUp() {
65 $this->_contactId
= $this->individualCreate();
66 $event = $this->eventCreate(['is_monetary' => 1]);
67 $this->_eventId
= $event['id'];
68 $this->_priceSetID
= $this->priceSetCreate();
69 CRM_Price_BAO_PriceSet
::addTo('civicrm_event', $this->_eventId
, $this->_priceSetID
);
70 $priceSet = CRM_Price_BAO_PriceSet
::getSetDetail($this->_priceSetID
, TRUE, FALSE);
71 $priceSet = CRM_Utils_Array
::value($this->_priceSetID
, $priceSet);
72 $this->_feeBlock
= CRM_Utils_Array
::value('fields', $priceSet);
76 * Clean up after test.
78 public function tearDown() {
79 $this->eventDelete($this->_eventId
);
80 $this->quickCleanUpFinancialEntities();
84 * Create an event with a price set.
89 * @todo resolve this with parent function.
91 protected function priceSetCreate($type = 'Radio') {
94 $paramsSet['title'] = 'Two Options' . substr(sha1(rand()), 0, 4);
95 $paramsSet['name'] = CRM_Utils_String
::titleToVar('Two Options') . substr(sha1(rand()), 0, 4);
96 $paramsSet['is_active'] = FALSE;
97 $paramsSet['extends'] = 1;
99 $priceSet = CRM_Price_BAO_PriceSet
::create($paramsSet);
101 if ($type == 'Text') {
103 'label' => 'Text Price Field',
104 'name' => CRM_Utils_String
::titleToVar('text_price_field'),
105 'html_type' => 'Text',
106 'option_label' => ['1' => 'Text Price Field'],
107 'option_name' => ['1' => CRM_Utils_String
::titleToVar('text_price_field')],
108 'option_weight' => ['1' => 1],
109 'option_amount' => ['1' => 10],
110 'option_count' => [1 => 1],
111 'is_display_amounts' => 1,
113 'options_per_line' => 1,
114 'is_active' => ['1' => 1],
115 'price_set_id' => $priceSet->id
,
117 'financial_type_id' => $this->getFinancialTypeId('Event Fee'),
122 'label' => 'Price Field',
123 'name' => CRM_Utils_String
::titleToVar('Two Options'),
124 'html_type' => 'Radio',
125 //'price' => $feeTotal,
126 'option_label' => ['1' => 'Expensive Room', '2' => "Cheap Room", '3' => 'Very Expensive', '4' => 'No Fee'],
127 'option_value' => ['1' => 'E', '2' => 'C', '3' => 'V', '4' => 'N'],
128 'option_name' => ['1' => 'Expensive', '2' => "Cheap", "3" => "Very Expensive", '3' => 'No Fee'],
129 'option_weight' => ['1' => 1, '2' => 2, '3' => 3, '4' => 4],
130 'option_amount' => ['1' => $this->_expensiveFee
, '2' => $this->_cheapFee
, '3' => $this->_veryExpensive
, '4' => $this->_noFee
],
131 'option_count' => [1 => 1, 2 => 1, 3 => 1, 4 => 1],
132 'is_display_amounts' => 1,
134 'options_per_line' => 1,
135 'is_active' => ['1' => 1],
136 'price_set_id' => $priceSet->id
,
138 'financial_type_id' => $this->getFinancialTypeId('Event Fee'),
141 $field = CRM_Price_BAO_PriceField
::create($paramsField);
142 $values = $this->callAPISuccess('PriceFieldValue', 'get', [
143 'price_field_id' => $field->id
,
144 'return' => ['id', 'label'],
146 foreach ($values['values'] as $value) {
147 switch ($value['label']) {
148 case 'Expensive Room':
149 $this->expensiveFeeValueID
= $value['id'];
153 $this->cheapFeeValueID
= $value['id'];
156 case 'Very Expensive':
157 $this->veryExpensiveFeeValueID
= $value['id'];
161 $this->noFeeID
= $value['id'];
167 $this->priceSetFieldID
= $field->id
;
168 return $priceSet->id
;
172 * Get the total for the invoice.
174 * @param int $contributionId
178 private function contributionInvoice($contributionId) {
180 SELECT SUM(line_total) total
181 FROM civicrm_line_item
182 WHERE contribution_id = {$contributionId}";
183 $dao = CRM_Core_DAO
::executeQuery($query);
185 $this->assertTrue($dao->fetch(), "Succeeded retrieving invoicetotal");
190 * Get the total income from the participant record.
192 * @param int $participantId
196 private function totalIncome($participantId) {
198 SELECT SUM(fi.amount) total
199 FROM civicrm_financial_item fi
200 INNER JOIN civicrm_line_item li ON li.id = fi.entity_id AND fi.entity_table = 'civicrm_line_item'
201 WHERE li.entity_table = 'civicrm_participant' AND li.entity_id = ${participantId}
203 $dao = CRM_Core_DAO
::executeQuery($query);
205 $this->assertTrue($dao->fetch(), "Succeeded retrieving total Income");
210 * Check the relevant entity balances.
212 * @param float $amount
214 private function balanceCheck($amount) {
215 $this->assertEquals($amount, $this->contributionInvoice($this->_contributionId
), "Invoice must a total of $amount");
216 $this->assertEquals($amount, $this->totalIncome($this->_participantId
), "The recorded income must be $amount ");
220 * Prepare records for editing.
222 public function registerParticipantAndPay($actualPaidAmt = NULL) {
227 'event_id' => $this->_eventId
,
228 'register_date' => date('Y-m-d') . " 00:00:00",
231 'source' => 'Event_' . $this->_eventId
,
232 'contact_id' => $this->_contactId
,
233 //'fee_level' => CRM_Core_DAO::VALUE_SEPARATOR.'Expensive Room'.CRM_Core_DAO::VALUE_SEPARATOR,
235 $participant = $this->callAPISuccess('Participant', 'create', $params);
236 $this->_participantId
= $participant['id'];
238 $actualPaidAmt = $actualPaidAmt ?
$actualPaidAmt : $this->_expensiveFee
;
240 $contributionParams = [
241 'total_amount' => $actualPaidAmt,
242 'source' => 'Testset with information',
244 'receipt_date' => date('Y-m-d') . " 00:00:00",
245 'contact_id' => $this->_contactId
,
246 'financial_type_id' => 4,
247 'payment_instrument_id' => 4,
248 'contribution_status_id' => 1,
249 'receive_date' => date('Y-m-d') . " 00:00:00",
251 'partial_payment_total' => $this->_expensiveFee
,
252 'partial_amount_to_pay' => $actualPaidAmt,
255 $contribution = $this->callAPISuccess('Contribution', 'create', $contributionParams);
256 $this->_contributionId
= $contribution['id'];
258 $this->callAPISuccess('participant_payment', 'create', [
259 'participant_id' => $this->_participantId
,
260 'contribution_id' => $this->_contributionId
,
263 $priceSetParams['price_' . $this->priceSetFieldID
] = $this->expensiveFeeValueID
;
265 $lineItems = CRM_Price_BAO_LineItem
::buildLineItemsForSubmittedPriceField($priceSetParams);
266 CRM_Price_BAO_PriceSet
::processAmount($this->_feeBlock
, $priceSetParams, $lineItems);
267 $lineItemVal[$this->_priceSetID
] = $lineItems;
268 CRM_Price_BAO_LineItem
::processPriceSet($participant['id'], $lineItemVal, $this->getContributionObject($contribution['id']), 'civicrm_participant');
269 $this->balanceCheck($this->_expensiveFee
);
270 $this->assertEquals(($this->_expensiveFee
- $actualPaidAmt), CRM_Contribute_BAO_Contribution
::getContributionBalance($this->_contributionId
));
274 public function testCRM19273() {
275 $this->registerParticipantAndPay();
277 $priceSetParams['price_' . $this->priceSetFieldID
] = $this->cheapFeeValueID
;
278 $lineItem = CRM_Price_BAO_LineItem
::getLineItems($this->_participantId
, 'participant');
279 CRM_Price_BAO_LineItem
::changeFeeSelections($priceSetParams, $this->_participantId
, 'participant', $this->_contributionId
, $this->_feeBlock
, $lineItem, $this->_expensiveFee
);
280 $this->balanceCheck($this->_cheapFee
);
282 $priceSetParams['price_' . $this->priceSetFieldID
] = $this->expensiveFeeValueID
;
283 $lineItem = CRM_Price_BAO_LineItem
::getLineItems($this->_participantId
, 'participant');
285 CRM_Price_BAO_LineItem
::changeFeeSelections($priceSetParams, $this->_participantId
, 'participant', $this->_contributionId
, $this->_feeBlock
, $lineItem, $this->_expensiveFee
);
287 $this->balanceCheck($this->_expensiveFee
);
289 $priceSetParams['price_' . $this->priceSetFieldID
] = $this->veryExpensiveFeeValueID
;
290 $lineItem = CRM_Price_BAO_LineItem
::getLineItems($this->_participantId
, 'participant');
291 CRM_Price_BAO_LineItem
::changeFeeSelections($priceSetParams, $this->_participantId
, 'participant', $this->_contributionId
, $this->_feeBlock
, $lineItem, $this->_expensiveFee
);
292 $this->balanceCheck($this->_veryExpensive
);
296 * CRM-21245: Test that Contribution status doesn't changed to 'Pending Refund' from 'Partially Paid' if the partially paid amount is lower then newly selected fee amount
298 public function testCRM21245() {
299 $this->registerParticipantAndPay(50);
300 $partiallyPaidContribuitonStatus = CRM_Core_PseudoConstant
::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Partially paid');
301 $this->assertEquals($this->callAPISuccessGetValue('Contribution', ['id' => $this->_contributionId
, 'return' => 'contribution_status_id']), $partiallyPaidContribuitonStatus);
303 $priceSetParams['price_' . $this->priceSetFieldID
] = $this->veryExpensiveFeeValueID
;
304 $lineItem = CRM_Price_BAO_LineItem
::getLineItems($this->_participantId
, 'participant');
305 CRM_Price_BAO_LineItem
::changeFeeSelections($priceSetParams, $this->_participantId
, 'participant', $this->_contributionId
, $this->_feeBlock
, $lineItem);
306 $this->assertEquals($this->callAPISuccessGetValue('Contribution', ['id' => $this->_contributionId
, 'return' => 'contribution_status_id']), $partiallyPaidContribuitonStatus);
310 * Test that proper financial items are recorded for cancelled line items
312 public function testCRM20611() {
313 $this->registerParticipantAndPay();
314 $actualPaidAmount = 100;
315 $priceSetParams['price_' . $this->priceSetFieldID
] = $this->expensiveFeeValueID
;
316 $lineItem = CRM_Price_BAO_LineItem
::getLineItems($this->participantID
, 'participant');
317 CRM_Price_BAO_LineItem
::changeFeeSelections($priceSetParams, $this->_participantId
, 'participant', $this->_contributionId
, $this->_feeBlock
, $lineItem);
318 $this->balanceCheck($this->_expensiveFee
);
319 $contributionBalance = ($this->_expensiveFee
- $actualPaidAmount);
320 $this->assertEquals($contributionBalance, CRM_Contribute_BAO_Contribution
::getContributionBalance($this->_contributionId
));
322 $priceSetParams['price_' . $this->priceSetFieldID
] = $this->cheapFeeValueID
;
323 $lineItem = CRM_Price_BAO_LineItem
::getLineItems($this->participantID
, 'participant');
324 CRM_Price_BAO_LineItem
::changeFeeSelections($priceSetParams, $this->_participantId
, 'participant', $this->_contributionId
, $this->_feeBlock
, $lineItem);
325 $this->balanceCheck($this->_cheapFee
);
326 $contributionBalance = ($this->_cheapFee
- $actualPaidAmount);
327 $this->assertEquals($contributionBalance, CRM_Contribute_BAO_Contribution
::getContributionBalance($this->_contributionId
));
329 $this->callAPISuccess('Payment', 'create', [
330 'contribution_id' => $this->_contributionId
,
331 'total_amount' => -120,
332 'payment_instrument_id' => 3,
333 'participant_id' => $this->_participantId
,
335 $contributionBalance +
= 120;
336 $this->assertEquals($contributionBalance, CRM_Contribute_BAO_Contribution
::getContributionBalance($this->_contributionId
));
338 // retrieve the cancelled line-item information
339 $cancelledLineItem = $this->callAPISuccessGetSingle('LineItem', [
340 'entity_table' => 'civicrm_participant',
341 'entity_id' => $this->_participantId
,
344 // retrieve the related financial lin-items
345 $financialItems = $this->callAPISuccess('FinancialItem', 'Get', [
346 'entity_id' => $cancelledLineItem['id'],
347 'entity_table' => 'civicrm_line_item',
349 $this->assertEquals($financialItems['count'], 2, 'Financial Items for Cancelled fee is not proper');
351 $contributionCompletedStatusID = CRM_Core_PseudoConstant
::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed');
352 $expectedAmount = 100.00;
353 foreach ($financialItems['values'] as $id => $financialItem) {
354 $this->assertEquals($expectedAmount, $financialItem['amount']);
355 $this->assertNotEmpty($financialItem['financial_account_id']);
356 $this->assertEquals($contributionCompletedStatusID, $financialItem['status_id']);
357 $expectedAmount = -$expectedAmount;
362 * Test to ensure that correct financial records are entered on text price field fee change on event registration
364 public function testCRM21513() {
365 $this->_priceSetID
= $this->priceSetCreate('Text');
366 CRM_Price_BAO_PriceSet
::addTo('civicrm_event', $this->_eventId
, $this->_priceSetID
);
367 $priceSet = CRM_Price_BAO_PriceSet
::getSetDetail($this->_priceSetID
, TRUE, FALSE);
368 $priceSet = CRM_Utils_Array
::value($this->_priceSetID
, $priceSet);
369 $this->_feeBlock
= CRM_Utils_Array
::value('fields', $priceSet);
375 'event_id' => $this->_eventId
,
376 'register_date' => date('Y-m-d') . " 00:00:00",
379 'source' => 'Event_' . $this->_eventId
,
380 'contact_id' => $this->_contactId
,
382 $participant = $this->callAPISuccess('Participant', 'create', $params);
383 $this->_participantId
= $participant['id'];
384 $contributionParams = [
385 'total_amount' => 10,
386 'source' => 'Testset with information',
388 'receipt_date' => date('Y-m-d') . " 00:00:00",
389 'contact_id' => $this->_contactId
,
390 'financial_type_id' => 4,
391 'payment_instrument_id' => 4,
392 'contribution_status_id' => CRM_Core_PseudoConstant
::getKey('CRM_Contribute_DAO_Contribution', 'contribution_status_id', 'Pending'),
393 'receive_date' => date('Y-m-d') . " 00:00:00",
397 $contribution = $this->callAPISuccess('Contribution', 'create', $contributionParams);
398 $this->_contributionId
= $contribution['id'];
400 $this->callAPISuccess('participant_payment', 'create', [
401 'participant_id' => $this->_participantId
,
402 'contribution_id' => $this->_contributionId
,
405 // CASE 1: Choose text price qty 1 (x$10 = $10 amount)
406 $priceSetParams['price_' . $this->priceSetFieldID
] = 1;
407 $lineItem = CRM_Price_BAO_LineItem
::getLineItems($this->_participantId
, 'participant');
408 CRM_Price_BAO_PriceSet
::processAmount($this->_feeBlock
, $priceSetParams, $lineItem);
409 $lineItemVal[$this->_priceSetID
] = $lineItem;
410 CRM_Price_BAO_LineItem
::processPriceSet($this->_participantId
, $lineItemVal, $this->getContributionObject($contribution['id']), 'civicrm_participant');
412 // CASE 2: Choose text price qty 3 (x$10 = $30 amount)
413 $priceSetParams['price_' . $this->priceSetFieldID
] = 3;
414 $lineItem = CRM_Price_BAO_LineItem
::getLineItems($participant['id'], 'participant');
415 CRM_Price_BAO_LineItem
::changeFeeSelections($priceSetParams, $participant['id'], 'participant', $this->_contributionId
, $this->_feeBlock
, $lineItem, 0);
417 // CASE 3: Choose text price qty 2 (x$10 = $20 amount)
418 $priceSetParams['price_' . $this->priceSetFieldID
] = 2;
419 $lineItem = CRM_Price_BAO_LineItem
::getLineItems($participant['id'], 'participant');
420 CRM_Price_BAO_LineItem
::changeFeeSelections($priceSetParams, $participant['id'], 'participant', $this->_contributionId
, $this->_feeBlock
, $lineItem, 0);
422 $financialItems = $this->callAPISuccess('FinancialItem', 'Get', [
423 'entity_table' => 'civicrm_line_item',
424 'entity_id' => ['IN' => array_keys($lineItem)],
428 $unpaidStatus = CRM_Core_PseudoConstant
::getKey('CRM_Financial_DAO_FinancialItem', 'status_id', 'Unpaid');
431 // when qty 1 is used
433 'status_id' => $unpaidStatus,
434 'entity_table' => 'civicrm_line_item',
438 // when qty 3 is used, add the surplus amount i.e. $30 - $10 = $20
440 'status_id' => $unpaidStatus,
441 'entity_table' => 'civicrm_line_item',
445 // when qty 2 is used, add the surplus amount i.e. $20 - $30 = -$10
447 'status_id' => $unpaidStatus,
448 'entity_table' => 'civicrm_line_item',
452 // Check if 3 financial items were recorded
453 $this->assertEquals(count($expectedResults), $financialItems['count']);
454 foreach ($expectedResults as $key => $expectedResult) {
455 foreach ($expectedResult as $column => $value) {
456 $this->assertEquals($expectedResult[$column], $financialItems['values'][$key][$column]);
460 $this->balanceCheck(20);
464 * CRM-17151: Test that Contribution status change to 'Completed' if balance is zero.
466 public function testCRM17151() {
467 $this->registerParticipantAndPay();
469 $contributionStatuses = CRM_Contribute_PseudoConstant
::contributionStatus(NULL, 'name');
470 $partiallyPaidStatusId = array_search('Partially paid', $contributionStatuses);
471 $pendingRefundStatusId = array_search('Pending refund', $contributionStatuses);
472 $completedStatusId = array_search('Completed', $contributionStatuses);
473 $this->assertDBCompareValue('CRM_Contribute_BAO_Contribution', $this->_contributionId
, 'contribution_status_id', 'id', $completedStatusId, 'Payment t be completed');
474 $priceSetParams['price_' . $this->priceSetFieldID
] = $this->cheapFeeValueID
;
475 $lineItem = CRM_Price_BAO_LineItem
::getLineItems($this->_participantId
, 'participant');
476 CRM_Price_BAO_LineItem
::changeFeeSelections($priceSetParams, $this->_participantId
, 'participant', $this->_contributionId
, $this->_feeBlock
, $lineItem);
477 $this->assertDBCompareValue('CRM_Contribute_BAO_Contribution', $this->_contributionId
, 'contribution_status_id', 'id', $pendingRefundStatusId, 'Contribution must be refunding');
478 $priceSetParams['price_' . $this->priceSetFieldID
] = $this->expensiveFeeValueID
;
479 $lineItem = CRM_Price_BAO_LineItem
::getLineItems($this->_participantId
, 'participant');
480 CRM_Price_BAO_LineItem
::changeFeeSelections($priceSetParams, $this->_participantId
, 'participant', $this->_contributionId
, $this->_feeBlock
, $lineItem);
481 $this->assertDBCompareValue('CRM_Contribute_BAO_Contribution', $this->_contributionId
, 'contribution_status_id', 'id', $completedStatusId, 'Contribution must, after complete payment be in state completed');
482 $priceSetParams['price_' . $this->priceSetFieldID
] = $this->veryExpensiveFeeValueID
;
483 $lineItem = CRM_Price_BAO_LineItem
::getLineItems($this->_participantId
, 'participant');
484 CRM_Price_BAO_LineItem
::changeFeeSelections($priceSetParams, $this->_participantId
, 'participant', $this->_contributionId
, $this->_feeBlock
, $lineItem);
485 $this->assertDBCompareValue('CRM_Contribute_BAO_Contribution', $this->_contributionId
, 'contribution_status_id', 'id', $partiallyPaidStatusId, 'Partial Paid');
489 * Test that recording a refund when fee selection is 0 works
491 public function testRefundWithFeeAmount0() {
492 $this->registerParticipantAndPay();
493 $actualPaidAmount = 100;
494 $priceSetParams['price_' . $this->priceSetFieldID
] = $this->expensiveFeeValueID
;
495 $lineItem = CRM_Price_BAO_LineItem
::getLineItems($this->participantID
, 'participant');
496 CRM_Price_BAO_LineItem
::changeFeeSelections($priceSetParams, $this->_participantId
, 'participant', $this->_contributionId
, $this->_feeBlock
, $lineItem);
497 $this->balanceCheck($this->_expensiveFee
);
498 $contributionBalance = ($this->_expensiveFee
- $actualPaidAmount);
499 $this->assertEquals($contributionBalance, CRM_Contribute_BAO_Contribution
::getContributionBalance($this->_contributionId
));
501 $priceSetParams['price_' . $this->priceSetFieldID
] = $this->noFeeID
;
502 $lineItem = CRM_Price_BAO_LineItem
::getLineItems($this->participantID
, 'participant');
503 CRM_Price_BAO_LineItem
::changeFeeSelections($priceSetParams, $this->_participantId
, 'participant', $this->_contributionId
, $this->_feeBlock
, $lineItem);
504 $this->balanceCheck($this->_noFee
);
505 $contributionBalance = ($this->_noFee
- $actualPaidAmount);
506 $this->assertEquals($contributionBalance, CRM_Contribute_BAO_Contribution
::getContributionBalance($this->_contributionId
));
508 $this->callAPISuccess('Payment', 'create', [
509 'contribution_id' => $this->_contributionId
,
510 'total_amount' => -100,
511 'payment_instrument_id' => 3,
512 'participant_id' => $this->_participantId
,
514 $contributionBalance +
= 100;
515 $this->assertEquals($contributionBalance, CRM_Contribute_BAO_Contribution
::getContributionBalance($this->_contributionId
));
517 // retrieve the cancelled line-item information
518 $cancelledLineItem = $this->callAPISuccessGetSingle('LineItem', [
519 'entity_table' => 'civicrm_participant',
520 'entity_id' => $this->_participantId
,
523 // retrieve the related financial lin-items
524 $financialItems = $this->callAPISuccess('FinancialItem', 'Get', [
525 'entity_id' => $cancelledLineItem['id'],
526 'entity_table' => 'civicrm_line_item',
528 $this->assertEquals($financialItems['count'], 2, 'Financial Items for Cancelled fee is not proper');
530 $contributionCompletedStatusID = CRM_Core_PseudoConstant
::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed');
531 $expectedAmount = 100.00;
532 foreach ($financialItems['values'] as $id => $financialItem) {
533 $this->assertEquals($expectedAmount, $financialItem['amount']);
534 $this->assertNotEmpty($financialItem['financial_account_id']);
535 $this->assertNotEmpty($financialItem['financial_account_id']);
536 $this->assertEquals($contributionCompletedStatusID, $financialItem['status_id']);
537 $expectedAmount = -$expectedAmount;