diff --git a/CRM/Price/BAO/LineItem.php b/CRM/Price/BAO/LineItem.php index adc6c57bd73b..93c6f3eb404e 100644 --- a/CRM/Price/BAO/LineItem.php +++ b/CRM/Price/BAO/LineItem.php @@ -639,7 +639,7 @@ public static function changeFeeSelections( if (!empty($requiredChanges['line_items_to_cancel']) || !empty($requiredChanges['line_items_to_update'])) { // @todo - this IF is to get this through PR merge but I suspect that it should not // be necessary & is masking something else. - $financialItemsArray = $lineItemObj->getReverseFinancialItemsToRecord( + $financialItemsArray = $lineItemObj->getAdjustedFinancialItemsToRecord( $entityID, $entityTable, $contributionId, @@ -703,9 +703,7 @@ public static function changeFeeSelections( $newFinancialItem = CRM_Financial_BAO_FinancialItem::create($updateFinancialItemInfoValues); // record reverse transaction only if Contribution is Completed because for pending refund or // partially paid we are already recording the surplus owed or refund amount - if (!empty($updateFinancialItemInfoValues['financialTrxn']) && ($contributionStatus == 'Completed' - ) - ) { + if (!empty($updateFinancialItemInfoValues['financialTrxn']) && ($contributionStatus == 'Completed')) { $updateFinancialItemInfoValues = array_merge($updateFinancialItemInfoValues['financialTrxn'], array( 'entity_id' => $newFinancialItem->id, 'entity_table' => 'civicrm_financial_item', @@ -720,6 +718,15 @@ public static function changeFeeSelections( )); unset($updateFinancialItemInfoValues['financialTrxn']); } + elseif (!empty($updateFinancialItemInfoValues['link-financial-trxn'])) { + civicrm_api3('EntityFinancialTrxn', 'create', array( + 'entity_id' => $newFinancialItem->id, + 'entity_table' => 'civicrm_financial_item', + 'financial_trxn_id' => $trxn->id, + 'amount' => $newFinancialItem->amount, + )); + unset($updateFinancialItemInfoValues['link-financial-trxn']); + } } } @@ -743,7 +750,7 @@ public static function changeFeeSelections( * @return array * List of formatted reverse Financial Items to be recorded */ - protected function getReverseFinancialItemsToRecord($entityID, $entityTable, $contributionID, $priceFieldValueIDsToCancel, $lineItemsToUpdate) { + protected function getAdjustedFinancialItemsToRecord($entityID, $entityTable, $contributionID, $priceFieldValueIDsToCancel, $lineItemsToUpdate) { $previousLineItems = CRM_Price_BAO_LineItem::getLineItems($entityID, str_replace('civicrm_', '', $entityTable)); $financialItemsArray = array(); @@ -773,6 +780,21 @@ protected function getReverseFinancialItemsToRecord($entityID, $entityTable, $co // INSERT negative financial_items for tax amount $financialItemsArray[$updateFinancialItemInfoValues['entity_id']] = $updateFinancialItemInfoValues; } + // INSERT a financial item to record surplus/lesser amount when a text price fee is changed + elseif (!empty($lineItemsToUpdate) && + $lineItemsToUpdate[$updateFinancialItemInfoValues['price_field_value_id']]['html_type'] == 'Text' && + $updateFinancialItemInfoValues['amount'] > 0 + ) { + // calculate the amount difference, considered as financial item amount + $updateFinancialItemInfoValues['amount'] = $lineItemsToUpdate[$updateFinancialItemInfoValues['price_field_value_id']]['line_total'] - $totalFinancialAmount; + // add a flag, later used to link financial trxn and this new financial item + $updateFinancialItemInfoValues['link-financial-trxn'] = TRUE; + if ($previousLineItems[$updateFinancialItemInfoValues['entity_id']]['tax_amount']) { + $updateFinancialItemInfoValues['tax']['amount'] = $lineItemsToUpdate[$updateFinancialItemInfoValues['entity_id']]['tax_amount'] - $previousLineItems[$updateFinancialItemInfoValues['entity_id']]['tax_amount']; + $updateFinancialItemInfoValues['tax']['description'] = $this->getSalesTaxTerm(); + } + $financialItemsArray[$updateFinancialItemInfoValues['entity_id']] = $updateFinancialItemInfoValues; + } } return $financialItemsArray; diff --git a/tests/phpunit/CRM/Dedupe/DedupeFinderTest.php b/tests/phpunit/CRM/Dedupe/DedupeFinderTest.php index d0718ed552cc..b20621e46a64 100644 --- a/tests/phpunit/CRM/Dedupe/DedupeFinderTest.php +++ b/tests/phpunit/CRM/Dedupe/DedupeFinderTest.php @@ -90,7 +90,7 @@ public function testCustomRule() { */ public function testSupervisedDupes() { $this->setupForGroupDedupe(); - $ruleGroup = $this->callAPISuccessGetSingle('RuleGroup', array('s_reserved' => 1, 'contact_type' => 'Individual', 'used' => 'Supervised')); + $ruleGroup = $this->callAPISuccessGetSingle('RuleGroup', array('is_reserved' => 1, 'contact_type' => 'Individual', 'used' => 'Supervised')); $foundDupes = CRM_Dedupe_Finder::dupesInGroup($ruleGroup['id'], $this->groupID); // ------------------------------------------------------------------------- // default dedupe rule: threshold = 20 => (First + Last + Email) Matches ( 1 pair ) diff --git a/tests/phpunit/CRM/Event/BAO/CRM19273Test.php b/tests/phpunit/CRM/Event/BAO/CRM19273Test.php index 88bb0839d90d..1caaa49a5a86 100644 --- a/tests/phpunit/CRM/Event/BAO/CRM19273Test.php +++ b/tests/phpunit/CRM/Event/BAO/CRM19273Test.php @@ -36,7 +36,7 @@ public function setUp() { $this->_contactId = $this->individualCreate(); $event = $this->eventCreate(array('is_monetary' => 1)); $this->_eventId = $event['id']; - $this->_priceSetID = $this->eventPriceSetCreate(); + $this->_priceSetID = $this->priceSetCreate(); CRM_Price_BAO_PriceSet::addTo('civicrm_event', $this->_eventId, $this->_priceSetID); $priceSet = CRM_Price_BAO_PriceSet::getSetDetail($this->_priceSetID, TRUE, FALSE); $priceSet = CRM_Utils_Array::value($this->_priceSetID, $priceSet); @@ -52,7 +52,6 @@ public function tearDown() { $this->quickCleanUpFinancialEntities(); } - /** * Remove default price field stuff. * @@ -75,40 +74,60 @@ protected function cleanup() { * Create an event with a price set. * * @todo resolve this with parent function. - * - * @param int $feeTotal - * @param int $minAmt * @param string $type * * @return int */ - protected function eventPriceSetCreate($feeTotal = 55, $minAmt = 0, $type = 'Text') { - $paramsSet['title'] = 'Two Options'; - $paramsSet['name'] = CRM_Utils_String::titleToVar('Two Options'); + protected function priceSetCreate($type = 'Radio') { + $feeTotal = 55; + $minAmt = 0; + $paramsSet['title'] = 'Two Options' . substr(sha1(rand()), 0, 4); + $paramsSet['name'] = CRM_Utils_String::titleToVar('Two Options') . substr(sha1(rand()), 0, 4); $paramsSet['is_active'] = FALSE; $paramsSet['extends'] = 1; $priceSet = CRM_Price_BAO_PriceSet::create($paramsSet); - $paramsField = array( - 'label' => 'Price Field', - 'name' => CRM_Utils_String::titleToVar('Two Options'), - 'html_type' => 'Radio', - //'price' => $feeTotal, - 'option_label' => array('1' => 'Expensive Room', '2' => "Cheap Room", '3' => 'Very Expensive'), - 'option_value' => array('1' => 'E', '2' => 'C', '3' => 'V'), - 'option_name' => array('1' => 'Expensive', '2' => "Cheap", "3" => "Very Expensive"), - 'option_weight' => array('1' => 1, '2' => 2, '3' => 3), - 'option_amount' => array('1' => $this->_expensiveFee, '2' => $this->_cheapFee, '3' => $this->_veryExpensive), - 'option_count' => array(1 => 1, 2 => 1, 3 => 1), - 'is_display_amounts' => 1, - 'weight' => 1, - 'options_per_line' => 1, - 'is_active' => array('1' => 1), - 'price_set_id' => $priceSet->id, - 'is_enter_qty' => 1, - 'financial_type_id' => $this->getFinancialTypeId('Event Fee'), - ); + if ($type == 'Text') { + $paramsField = array( + 'label' => 'Text Price Field', + 'name' => CRM_Utils_String::titleToVar('text_price_field'), + 'html_type' => 'Text', + 'option_label' => array('1' => 'Text Price Field'), + 'option_name' => array('1' => CRM_Utils_String::titleToVar('text_price_field')), + 'option_weight' => array('1' => 1), + 'option_amount' => array('1' => 10), + 'option_count' => array(1 => 1), + 'is_display_amounts' => 1, + 'weight' => 1, + 'options_per_line' => 1, + 'is_active' => array('1' => 1), + 'price_set_id' => $priceSet->id, + 'is_enter_qty' => 1, + 'financial_type_id' => $this->getFinancialTypeId('Event Fee'), + ); + } + else { + $paramsField = array( + 'label' => 'Price Field', + 'name' => CRM_Utils_String::titleToVar('Two Options'), + 'html_type' => 'Radio', + //'price' => $feeTotal, + 'option_label' => array('1' => 'Expensive Room', '2' => "Cheap Room", '3' => 'Very Expensive'), + 'option_value' => array('1' => 'E', '2' => 'C', '3' => 'V'), + 'option_name' => array('1' => 'Expensive', '2' => "Cheap", "3" => "Very Expensive"), + 'option_weight' => array('1' => 1, '2' => 2, '3' => 3), + 'option_amount' => array('1' => $this->_expensiveFee, '2' => $this->_cheapFee, '3' => $this->_veryExpensive), + 'option_count' => array(1 => 1, 2 => 1, 3 => 1), + 'is_display_amounts' => 1, + 'weight' => 1, + 'options_per_line' => 1, + 'is_active' => array('1' => 1), + 'price_set_id' => $priceSet->id, + 'is_enter_qty' => 1, + 'financial_type_id' => $this->getFinancialTypeId('Event Fee'), + ); + } $field = CRM_Price_BAO_PriceField::create($paramsField); $this->priceSetFieldID = $field->id; return $priceSet->id; @@ -159,7 +178,6 @@ private function totalIncome($participantId) { private function balanceCheck($amount) { $this->assertEquals($amount, $this->contributionInvoice($this->_contributionId), "Invoice must a total of $amount"); $this->assertEquals($amount, $this->totalIncome($this->_participantId), "The recorded income must be $amount "); - $this->assertEquals($amount, $this->totalIncome($this->_contributionId), "The accumulated assets must be $amount "); } /** @@ -278,4 +296,113 @@ public function testCRM20611() { } } + /** + * Test to ensure that correct financial records are entered on text price field fee change on event registration + */ + public function testCRM21513() { + $this->quickCleanup( + array( + 'civicrm_price_field_value', + 'civicrm_price_field', + 'civicrm_price_set', + 'civicrm_line_item', + 'civicrm_financial_item', + ) + ); + + $this->_priceSetID = $this->priceSetCreate('Text'); + CRM_Price_BAO_PriceSet::addTo('civicrm_event', $this->_eventId, $this->_priceSetID); + $priceSet = CRM_Price_BAO_PriceSet::getSetDetail($this->_priceSetID, TRUE, FALSE); + $priceSet = CRM_Utils_Array::value($this->_priceSetID, $priceSet); + $this->_feeBlock = CRM_Utils_Array::value('fields', $priceSet); + + $params = array( + 'send_receipt' => 1, + 'is_test' => 0, + 'is_pay_later' => 0, + 'event_id' => $this->_eventId, + 'register_date' => date('Y-m-d') . " 00:00:00", + 'role_id' => 1, + 'status_id' => 1, + 'source' => 'Event_' . $this->_eventId, + 'contact_id' => $this->_contactId, + ); + $participant = $this->callAPISuccess('Participant', 'create', $params); + $contributionParams = array( + 'total_amount' => 10, + 'source' => 'Testset with information', + 'currency' => 'USD', + 'non_deductible_amount' => 'null', + 'receipt_date' => date('Y-m-d') . " 00:00:00", + 'contact_id' => $this->_contactId, + 'financial_type_id' => 4, + 'payment_instrument_id' => 4, + 'contribution_status_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_DAO_Contribution', 'contribution_status_id', 'Pending'), + 'receive_date' => date('Y-m-d') . " 00:00:00", + 'skipLineItem' => 1, + ); + + $contribution = CRM_Contribute_BAO_Contribution::create($contributionParams); + $this->_contributionId = $contribution->id; + + $this->callAPISuccess('participant_payment', 'create', array( + 'participant_id' => $this->_participantId, + 'contribution_id' => $this->_contributionId, + )); + + // CASE 1: Choose text price qty 1 (x$10 = $10 amount) + $priceSetParams['price_1'] = 1; + $lineItem = CRM_Price_BAO_LineItem::getLineItems($this->_participantId, 'participant'); + CRM_Price_BAO_PriceSet::processAmount($this->_feeBlock, $priceSetParams, $lineItem); + $lineItemVal[$this->_priceSetID] = $lineItem; + CRM_Price_BAO_LineItem::processPriceSet($this->_participantId, $lineItemVal, $contribution, 'civicrm_participant'); + + // CASE 2: Choose text price qty 3 (x$10 = $30 amount) + $priceSetParams['price_1'] = 3; + $lineItem = CRM_Price_BAO_LineItem::getLineItems($this->_participantId, 'participant'); + CRM_Price_BAO_LineItem::changeFeeSelections($priceSetParams, $this->_participantId, 'participant', $this->_contributionId, $this->_feeBlock, $lineItem, 0); + + // CASE 3: Choose text price qty 2 (x$10 = $20 amount) + $priceSetParams['price_1'] = 2; + $lineItem = CRM_Price_BAO_LineItem::getLineItems($this->_participantId, 'participant'); + CRM_Price_BAO_LineItem::changeFeeSelections($priceSetParams, $this->_participantId, 'participant', $this->_contributionId, $this->_feeBlock, $lineItem, 0); + + $financialItems = $this->callAPISuccess('FinancialItem', 'Get', array( + 'entity_table' => 'civicrm_line_item', + 'entity_id' => array('IN' => array_keys($lineItem)), + 'sequential' => 1, + )); + + $unpaidStatus = CRM_Core_PseudoConstant::getKey('CRM_Financial_DAO_FinancialItem', 'status_id', 'Unpaid'); + $expectedResults = array( + array( + 'amount' => 10.00, // when qty 1 is used + 'status_id' => $unpaidStatus, + 'entity_table' => 'civicrm_line_item', + 'entity_id' => 1, + ), + array( + 'amount' => 20.00, // when qty 3 is used, add the surplus amount i.e. $30 - $10 = $20 + 'status_id' => $unpaidStatus, + 'entity_table' => 'civicrm_line_item', + 'entity_id' => 1, + ), + array( + 'amount' => -10.00, // when qty 2 is used, add the surplus amount i.e. $20 - $30 = -$10 + 'status_id' => $unpaidStatus, + 'entity_table' => 'civicrm_line_item', + 'entity_id' => 1, + ), + ); + // Check if 3 financial items were recorded + $this->assertEquals(count($expectedResults), $financialItems['count']); + foreach ($expectedResults as $key => $expectedResult) { + foreach ($expectedResult as $column => $value) { + $this->assertEquals($expectedResult[$column], $financialItems['values'][$key][$column]); + } + } + + $this->balanceCheck(20); + } + }