3 * Class CRM_Event_BAO_AdditionalPaymentTest
6 class CRM_Event_BAO_ChangeFeeSelectionTest
extends CiviUnitTestCase
{
8 protected $_priceSetID;
9 protected $_cheapFee = 80;
10 protected $_expensiveFee = 100;
11 protected $_veryExpensive = 120;
12 protected $expensiveFeeValueID;
13 protected $cheapFeeValueID;
14 protected $veryExpensiveFeeValueID;
19 protected $contributionID;
24 protected $participantID;
31 protected $priceSetFieldID;
51 public function setUp() {
53 $this->_contactId
= $this->individualCreate();
54 $event = $this->eventCreate(array('is_monetary' => 1));
55 $this->_eventId
= $event['id'];
56 $this->_priceSetID
= $this->priceSetCreate();
57 CRM_Price_BAO_PriceSet
::addTo('civicrm_event', $this->_eventId
, $this->_priceSetID
);
58 $priceSet = CRM_Price_BAO_PriceSet
::getSetDetail($this->_priceSetID
, TRUE, FALSE);
59 $priceSet = CRM_Utils_Array
::value($this->_priceSetID
, $priceSet);
60 $this->_feeBlock
= CRM_Utils_Array
::value('fields', $priceSet);
64 * Clean up after test.
66 public function tearDown() {
67 $this->eventDelete($this->_eventId
);
68 $this->quickCleanUpFinancialEntities();
72 * Create an event with a price set.
74 * @todo resolve this with parent function.
79 protected function priceSetCreate($type = 'Radio') {
82 $paramsSet['title'] = 'Two Options' . substr(sha1(rand()), 0, 4);
83 $paramsSet['name'] = CRM_Utils_String
::titleToVar('Two Options') . substr(sha1(rand()), 0, 4);
84 $paramsSet['is_active'] = FALSE;
85 $paramsSet['extends'] = 1;
87 $priceSet = CRM_Price_BAO_PriceSet
::create($paramsSet);
89 if ($type == 'Text') {
91 'label' => 'Text Price Field',
92 'name' => CRM_Utils_String
::titleToVar('text_price_field'),
93 'html_type' => 'Text',
94 'option_label' => array('1' => 'Text Price Field'),
95 'option_name' => array('1' => CRM_Utils_String
::titleToVar('text_price_field')),
96 'option_weight' => array('1' => 1),
97 'option_amount' => array('1' => 10),
98 'option_count' => array(1 => 1),
99 'is_display_amounts' => 1,
101 'options_per_line' => 1,
102 'is_active' => array('1' => 1),
103 'price_set_id' => $priceSet->id
,
105 'financial_type_id' => $this->getFinancialTypeId('Event Fee'),
109 $paramsField = array(
110 'label' => 'Price Field',
111 'name' => CRM_Utils_String
::titleToVar('Two Options'),
112 'html_type' => 'Radio',
113 //'price' => $feeTotal,
114 'option_label' => array('1' => 'Expensive Room', '2' => "Cheap Room", '3' => 'Very Expensive'),
115 'option_value' => array('1' => 'E', '2' => 'C', '3' => 'V'),
116 'option_name' => array('1' => 'Expensive', '2' => "Cheap", "3" => "Very Expensive"),
117 'option_weight' => array('1' => 1, '2' => 2, '3' => 3),
118 'option_amount' => array('1' => $this->_expensiveFee
, '2' => $this->_cheapFee
, '3' => $this->_veryExpensive
),
119 'option_count' => array(1 => 1, 2 => 1, 3 => 1),
120 'is_display_amounts' => 1,
122 'options_per_line' => 1,
123 'is_active' => array('1' => 1),
124 'price_set_id' => $priceSet->id
,
126 'financial_type_id' => $this->getFinancialTypeId('Event Fee'),
129 $field = CRM_Price_BAO_PriceField
::create($paramsField);
130 $values = $this->callAPISuccess('PriceFieldValue', 'get', [
131 'price_field_id' => $field->id
,
132 'return' => ['id', 'label']
134 foreach ($values['values'] as $value) {
135 switch ($value['label']) {
136 case 'Expensive Room':
137 $this->expensiveFeeValueID
= $value['id'];
141 $this->cheapFeeValueID
= $value['id'];
144 case 'Very Expensive':
145 $this->veryExpensiveFeeValueID
= $value['id'];
150 $this->priceSetFieldID
= $field->id
;
151 return $priceSet->id
;
155 * Get the total for the invoice.
157 * @param int $contributionId
160 private function contributionInvoice($contributionId) {
162 SELECT SUM(line_total) total
163 FROM civicrm_line_item
164 WHERE contribution_id = {$contributionId}";
165 $dao = CRM_Core_DAO
::executeQuery($query);
167 $this->assertTrue($dao->fetch(), "Succeeded retrieving invoicetotal");
172 * Get the total income from the participant record.
174 * @param int $participantId
178 private function totalIncome($participantId) {
180 SELECT SUM(fi.amount) total
181 FROM civicrm_financial_item fi
182 INNER JOIN civicrm_line_item li ON li.id = fi.entity_id AND fi.entity_table = 'civicrm_line_item'
183 WHERE li.entity_table = 'civicrm_participant' AND li.entity_id = ${participantId}
185 $dao = CRM_Core_DAO
::executeQuery($query);
187 $this->assertTrue($dao->fetch(), "Succeeded retrieving total Income");
192 * Check the relevant entity balances.
194 * @param float $amount
196 private function balanceCheck($amount) {
197 $this->assertEquals($amount, $this->contributionInvoice($this->_contributionId
), "Invoice must a total of $amount");
198 $this->assertEquals($amount, $this->totalIncome($this->_participantId
), "The recorded income must be $amount ");
202 * Prepare records for editing.
204 public function registerParticipantAndPay($actualPaidAmt = NULL) {
209 'event_id' => $this->_eventId
,
210 'register_date' => date('Y-m-d') . " 00:00:00",
213 'source' => 'Event_' . $this->_eventId
,
214 'contact_id' => $this->_contactId
,
215 //'fee_level' => CRM_Core_DAO::VALUE_SEPARATOR.'Expensive Room'.CRM_Core_DAO::VALUE_SEPARATOR,
217 $participant = $this->callAPISuccess('Participant', 'create', $params);
218 $this->_participantId
= $participant['id'];
220 $actualPaidAmt = $actualPaidAmt ?
$actualPaidAmt : $this->_expensiveFee
;
222 $contributionParams = array(
223 'total_amount' => $actualPaidAmt,
224 'source' => 'Testset with information',
226 'receipt_date' => date('Y-m-d') . " 00:00:00",
227 'contact_id' => $this->_contactId
,
228 'financial_type_id' => 4,
229 'payment_instrument_id' => 4,
230 'contribution_status_id' => 1,
231 'receive_date' => date('Y-m-d') . " 00:00:00",
233 'partial_payment_total' => $this->_expensiveFee
,
234 'partial_amount_to_pay' => $actualPaidAmt,
237 $contribution = $this->callAPISuccess('Contribution', 'create', $contributionParams);
238 $this->_contributionId
= $contribution['id'];
240 $this->callAPISuccess('participant_payment', 'create', array(
241 'participant_id' => $this->_participantId
,
242 'contribution_id' => $this->_contributionId
,
245 $priceSetParams['price_' . $this->priceSetFieldID
] = $this->expensiveFeeValueID
;
247 $lineItems = CRM_Price_BAO_LineItem
::buildLineItemsForSubmittedPriceField($priceSetParams);
248 CRM_Price_BAO_PriceSet
::processAmount($this->_feeBlock
, $priceSetParams, $lineItems);
249 $lineItemVal[$this->_priceSetID
] = $lineItems;
250 CRM_Price_BAO_LineItem
::processPriceSet($participant['id'], $lineItemVal, $this->getContributionObject($contribution['id']), 'civicrm_participant');
251 $this->balanceCheck($this->_expensiveFee
);
252 $this->assertEquals(($this->_expensiveFee
- $actualPaidAmt), CRM_Contribute_BAO_Contribution
::getContributionBalance($this->_contributionId
));
256 public function testCRM19273() {
257 $this->registerParticipantAndPay();
259 $priceSetParams['price_' . $this->priceSetFieldID
] = $this->cheapFeeValueID
;
260 $lineItem = CRM_Price_BAO_LineItem
::getLineItems($this->_participantId
, 'participant');
261 CRM_Price_BAO_LineItem
::changeFeeSelections($priceSetParams, $this->_participantId
, 'participant', $this->_contributionId
, $this->_feeBlock
, $lineItem, $this->_expensiveFee
);
262 $this->balanceCheck($this->_cheapFee
);
264 $priceSetParams['price_' . $this->priceSetFieldID
] = $this->expensiveFeeValueID
;
265 $lineItem = CRM_Price_BAO_LineItem
::getLineItems($this->_participantId
, 'participant');
267 CRM_Price_BAO_LineItem
::changeFeeSelections($priceSetParams, $this->_participantId
, 'participant', $this->_contributionId
, $this->_feeBlock
, $lineItem, $this->_expensiveFee
);
269 $this->balanceCheck($this->_expensiveFee
);
271 $priceSetParams['price_' . $this->priceSetFieldID
] = $this->veryExpensiveFeeValueID
;
272 $lineItem = CRM_Price_BAO_LineItem
::getLineItems($this->_participantId
, 'participant');
273 CRM_Price_BAO_LineItem
::changeFeeSelections($priceSetParams, $this->_participantId
, 'participant', $this->_contributionId
, $this->_feeBlock
, $lineItem, $this->_expensiveFee
);
274 $this->balanceCheck($this->_veryExpensive
);
278 * 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
280 public function testCRM21245() {
281 $this->registerParticipantAndPay(50);
282 $partiallyPaidContribuitonStatus = CRM_Core_PseudoConstant
::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Partially paid');
283 $this->assertEquals($this->callAPISuccessGetValue('Contribution', array('id' => $this->_contributionId
, 'return' => 'contribution_status_id')), $partiallyPaidContribuitonStatus);
285 $priceSetParams['price_' . $this->priceSetFieldID
] = $this->veryExpensiveFeeValueID
;
286 $lineItem = CRM_Price_BAO_LineItem
::getLineItems($this->_participantId
, 'participant');
287 CRM_Price_BAO_LineItem
::changeFeeSelections($priceSetParams, $this->_participantId
, 'participant', $this->_contributionId
, $this->_feeBlock
, $lineItem);
288 $this->assertEquals($this->callAPISuccessGetValue('Contribution', array('id' => $this->_contributionId
, 'return' => 'contribution_status_id')), $partiallyPaidContribuitonStatus);
292 * Test that proper financial items are recorded for cancelled line items
294 public function testCRM20611() {
295 $this->registerParticipantAndPay();
296 $actualPaidAmount = 100;
297 $priceSetParams['price_' . $this->priceSetFieldID
] = $this->expensiveFeeValueID
;
298 $lineItem = CRM_Price_BAO_LineItem
::getLineItems($this->participantID
, 'participant');
299 CRM_Price_BAO_LineItem
::changeFeeSelections($priceSetParams, $this->_participantId
, 'participant', $this->_contributionId
, $this->_feeBlock
, $lineItem);
300 $this->balanceCheck($this->_expensiveFee
);
301 $contributionBalance = ($this->_expensiveFee
- $actualPaidAmount);
302 $this->assertEquals($contributionBalance, CRM_Contribute_BAO_Contribution
::getContributionBalance($this->_contributionId
));
304 $priceSetParams['price_' . $this->priceSetFieldID
] = $this->cheapFeeValueID
;
305 $lineItem = CRM_Price_BAO_LineItem
::getLineItems($this->participantID
, 'participant');
306 CRM_Price_BAO_LineItem
::changeFeeSelections($priceSetParams, $this->_participantId
, 'participant', $this->_contributionId
, $this->_feeBlock
, $lineItem);
307 $this->balanceCheck($this->_cheapFee
);
308 $contributionBalance = ($this->_cheapFee
- $actualPaidAmount);
309 $this->assertEquals($contributionBalance, CRM_Contribute_BAO_Contribution
::getContributionBalance($this->_contributionId
));
311 //Complete the refund payment.
312 $submittedValues = array(
313 'total_amount' => 120,
314 'payment_instrument_id' => 3,
316 CRM_Contribute_BAO_Contribution
::recordAdditionalPayment($this->_contributionId
, $submittedValues, 'refund', $this->_participantId
);
317 $contributionBalance +
= 120;
318 $this->assertEquals($contributionBalance, CRM_Contribute_BAO_Contribution
::getContributionBalance($this->_contributionId
));
320 // retrieve the cancelled line-item information
321 $cancelledLineItem = $this->callAPISuccessGetSingle('LineItem', array(
322 'entity_table' => 'civicrm_participant',
323 'entity_id' => $this->_participantId
,
326 // retrieve the related financial lin-items
327 $financialItems = $this->callAPISuccess('FinancialItem', 'Get', array(
328 'entity_id' => $cancelledLineItem['id'],
329 'entity_table' => 'civicrm_line_item',
331 $this->assertEquals($financialItems['count'], 2, 'Financial Items for Cancelled fee is not proper');
333 $contributionCompletedStatusID = CRM_Core_PseudoConstant
::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed');
334 $expectedAmount = 100.00;
335 foreach ($financialItems['values'] as $id => $financialItem) {
336 $this->assertEquals($expectedAmount, $financialItem['amount']);
337 $this->assertNotEmpty($financialItem['financial_account_id']);
338 $this->assertEquals($contributionCompletedStatusID, $financialItem['status_id']);
339 $expectedAmount = -$expectedAmount;
344 * Test to ensure that correct financial records are entered on text price field fee change on event registration
346 public function testCRM21513() {
347 $this->_priceSetID
= $this->priceSetCreate('Text');
348 CRM_Price_BAO_PriceSet
::addTo('civicrm_event', $this->_eventId
, $this->_priceSetID
);
349 $priceSet = CRM_Price_BAO_PriceSet
::getSetDetail($this->_priceSetID
, TRUE, FALSE);
350 $priceSet = CRM_Utils_Array
::value($this->_priceSetID
, $priceSet);
351 $this->_feeBlock
= CRM_Utils_Array
::value('fields', $priceSet);
357 'event_id' => $this->_eventId
,
358 'register_date' => date('Y-m-d') . " 00:00:00",
361 'source' => 'Event_' . $this->_eventId
,
362 'contact_id' => $this->_contactId
,
364 $participant = $this->callAPISuccess('Participant', 'create', $params);
365 $this->_participantId
= $participant['id'];
366 $contributionParams = array(
367 'total_amount' => 10,
368 'source' => 'Testset with information',
370 'receipt_date' => date('Y-m-d') . " 00:00:00",
371 'contact_id' => $this->_contactId
,
372 'financial_type_id' => 4,
373 'payment_instrument_id' => 4,
374 'contribution_status_id' => CRM_Core_PseudoConstant
::getKey('CRM_Contribute_DAO_Contribution', 'contribution_status_id', 'Pending'),
375 'receive_date' => date('Y-m-d') . " 00:00:00",
379 $contribution = $this->callAPISuccess('Contribution', 'create', $contributionParams);
380 $this->_contributionId
= $contribution['id'];
382 $this->callAPISuccess('participant_payment', 'create', array(
383 'participant_id' => $this->_participantId
,
384 'contribution_id' => $this->_contributionId
,
387 // CASE 1: Choose text price qty 1 (x$10 = $10 amount)
388 $priceSetParams['price_' . $this->priceSetFieldID
] = 1;
389 $lineItem = CRM_Price_BAO_LineItem
::getLineItems($this->_participantId
, 'participant');
390 CRM_Price_BAO_PriceSet
::processAmount($this->_feeBlock
, $priceSetParams, $lineItem);
391 $lineItemVal[$this->_priceSetID
] = $lineItem;
392 CRM_Price_BAO_LineItem
::processPriceSet($this->_participantId
, $lineItemVal, $this->getContributionObject($contribution['id']), 'civicrm_participant');
394 // CASE 2: Choose text price qty 3 (x$10 = $30 amount)
395 $priceSetParams['price_' . $this->priceSetFieldID
] = 3;
396 $lineItem = CRM_Price_BAO_LineItem
::getLineItems($participant['id'], 'participant');
397 CRM_Price_BAO_LineItem
::changeFeeSelections($priceSetParams, $participant['id'], 'participant', $this->_contributionId
, $this->_feeBlock
, $lineItem, 0);
399 // CASE 3: Choose text price qty 2 (x$10 = $20 amount)
400 $priceSetParams['price_' . $this->priceSetFieldID
] = 2;
401 $lineItem = CRM_Price_BAO_LineItem
::getLineItems($participant['id'], 'participant');
402 CRM_Price_BAO_LineItem
::changeFeeSelections($priceSetParams, $participant['id'], 'participant', $this->_contributionId
, $this->_feeBlock
, $lineItem, 0);
404 $financialItems = $this->callAPISuccess('FinancialItem', 'Get', array(
405 'entity_table' => 'civicrm_line_item',
406 'entity_id' => array('IN' => array_keys($lineItem)),
410 $unpaidStatus = CRM_Core_PseudoConstant
::getKey('CRM_Financial_DAO_FinancialItem', 'status_id', 'Unpaid');
411 $expectedResults = array(
413 'amount' => 10.00, // when qty 1 is used
414 'status_id' => $unpaidStatus,
415 'entity_table' => 'civicrm_line_item',
419 'amount' => 20.00, // when qty 3 is used, add the surplus amount i.e. $30 - $10 = $20
420 'status_id' => $unpaidStatus,
421 'entity_table' => 'civicrm_line_item',
425 'amount' => -10.00, // when qty 2 is used, add the surplus amount i.e. $20 - $30 = -$10
426 'status_id' => $unpaidStatus,
427 'entity_table' => 'civicrm_line_item',
431 // Check if 3 financial items were recorded
432 $this->assertEquals(count($expectedResults), $financialItems['count']);
433 foreach ($expectedResults as $key => $expectedResult) {
434 foreach ($expectedResult as $column => $value) {
435 $this->assertEquals($expectedResult[$column], $financialItems['values'][$key][$column]);
439 $this->balanceCheck(20);
443 * CRM-17151: Test that Contribution status change to 'Completed' if balance is zero.
445 public function testCRM17151() {
446 $this->registerParticipantAndPay();
448 $contributionStatuses = CRM_Contribute_PseudoConstant
::contributionStatus(NULL, 'name');
449 $partiallyPaidStatusId = array_search('Partially paid', $contributionStatuses);
450 $pendingRefundStatusId = array_search('Pending refund', $contributionStatuses);
451 $completedStatusId = array_search('Completed', $contributionStatuses);
452 $this->assertDBCompareValue('CRM_Contribute_BAO_Contribution', $this->_contributionId
, 'contribution_status_id', 'id', $completedStatusId, 'Payment t be completed');
453 $priceSetParams['price_' . $this->priceSetFieldID
] = $this->cheapFeeValueID
;
454 $lineItem = CRM_Price_BAO_LineItem
::getLineItems($this->_participantId
, 'participant');
455 CRM_Price_BAO_LineItem
::changeFeeSelections($priceSetParams, $this->_participantId
, 'participant', $this->_contributionId
, $this->_feeBlock
, $lineItem);
456 $this->assertDBCompareValue('CRM_Contribute_BAO_Contribution', $this->_contributionId
, 'contribution_status_id', 'id', $pendingRefundStatusId, 'Contribution must be refunding');
457 $priceSetParams['price_' . $this->priceSetFieldID
] = $this->expensiveFeeValueID
;
458 $lineItem = CRM_Price_BAO_LineItem
::getLineItems($this->_participantId
, 'participant');
459 CRM_Price_BAO_LineItem
::changeFeeSelections($priceSetParams, $this->_participantId
, 'participant', $this->_contributionId
, $this->_feeBlock
, $lineItem);
460 $this->assertDBCompareValue('CRM_Contribute_BAO_Contribution', $this->_contributionId
, 'contribution_status_id', 'id', $completedStatusId, 'Contribution must, after complete payment be in state completed');
461 $priceSetParams['price_' . $this->priceSetFieldID
] = $this->veryExpensiveFeeValueID
;
462 $lineItem = CRM_Price_BAO_LineItem
::getLineItems($this->_participantId
, 'participant');
463 CRM_Price_BAO_LineItem
::changeFeeSelections($priceSetParams, $this->_participantId
, 'participant', $this->_contributionId
, $this->_feeBlock
, $lineItem);
464 $this->assertDBCompareValue('CRM_Contribute_BAO_Contribution', $this->_contributionId
, 'contribution_status_id', 'id', $partiallyPaidStatusId, 'Partial Paid');