The support covers the relationship 'falling back' to current behaviour if a Contra/Credit Revenue Account is
entry is not configured for the financial type.
'In Progress',
);
if (in_array($contributionStatus, $pendingStatus)) {
- $relationTypeId = key(CRM_Core_PseudoConstant::accountOptionValues('account_relationship', NULL, " AND v.name LIKE 'Accounts Receivable Account is' "));
- $params['to_financial_account_id'] = CRM_Contribute_PseudoConstant::financialAccountType($params['financial_type_id'], $relationTypeId);
+ $params['to_financial_account_id'] = CRM_Financial_BAO_FinancialAccount::getFinancialAccountForFinancialTypeByRelationship(
+ $params['financial_type_id'],
+ 'Accounts Receivable Account is'
+ );
}
elseif (!empty($params['payment_processor'])) {
$params['to_financial_account_id'] = CRM_Financial_BAO_FinancialTypeAccount::getFinancialAccount($params['payment_processor'], 'civicrm_payment_processor', 'financial_account_id');
foreach ($params['line_item'] as $fieldId => $fields) {
foreach ($fields as $fieldValueId => $fieldValues) {
$prevParams['entity_id'] = $fieldValues['id'];
- $prevfinancialItem = CRM_Financial_BAO_FinancialItem::retrieve($prevParams, CRM_Core_DAO::$_nullArray);
+ $prevFinancialItem = CRM_Financial_BAO_FinancialItem::retrieve($prevParams, CRM_Core_DAO::$_nullArray);
$receiveDate = CRM_Utils_Date::isoToMysql($params['prevContribution']->receive_date);
if ($params['contribution']->receive_date) {
$receiveDate = CRM_Utils_Date::isoToMysql($params['contribution']->receive_date);
}
- $financialAccount = $prevfinancialItem->financial_account_id;
- if (!empty($params['financial_account_id'])) {
- $financialAccount = $params['financial_account_id'];
- }
+ $financialAccount = self::getFinancialAccountForStatusChangeTrxn($params, $prevFinancialItem);
$currency = $params['prevContribution']->currency;
if ($params['contribution']->currency) {
'contact_id' => $params['prevContribution']->contact_id,
'currency' => $currency,
'amount' => $amount,
- 'description' => $prevfinancialItem->description,
- 'status_id' => $prevfinancialItem->status_id,
+ 'description' => $prevFinancialItem->description,
+ 'status_id' => $prevFinancialItem->status_id,
'financial_account_id' => $financialAccount,
'entity_table' => 'civicrm_line_item',
'entity_id' => $fieldValues['id'],
}
}
+ /**
+ * Get the financial account for the item associated with the new transaction.
+ *
+ * @param array $params
+ * @param CRM_Financial_BAO_FinancialItem $prevFinancialItem
+ *
+ * @return int
+ */
+ public static function getFinancialAccountForStatusChangeTrxn($params, $prevFinancialItem) {
+
+ if (!empty($params['financial_account_id'])) {
+ return $params['financial_account_id'];
+ }
+ $contributionStatus = CRM_Contribute_PseudoConstant::contributionStatus($params['contribution_status_id'], 'name');
+ $preferredAccountsRelationships = array(
+ 'Refunded' => 'Credit/Contra Revenue Account is',
+ 'Chargeback' => 'Chargeback Account is',
+ );
+ if (in_array($contributionStatus, array_keys($preferredAccountsRelationships))) {
+ $financialTypeID = !empty($params['financial_type_id']) ? $params['financial_type_id'] : $params['prevContribution']->financial_type_id;
+ return CRM_Financial_BAO_FinancialAccount::getFinancialAccountForFinancialTypeByRelationship(
+ $financialTypeID,
+ $preferredAccountsRelationships[$contributionStatus]
+ );
+ }
+ return $prevFinancialItem->financial_account_id;
+ }
+
}
return CRM_Core_DAO::singleValueQuery($query, $params);
}
+ /**
+ * Get the Financial Account for a Financial Type Relationship Combo.
+ *
+ * Note that some relationships are optionally configured - so far
+ * Chargeback and Credit / Contra. Since these are the only 2 currently Income
+ * is an appropriate fallback. In future it might make sense to extend the logic.
+ *
+ * Note that we avoid the CRM_Core_PseudoConstant function as it stores one
+ * account per financial type and is unreliable.
+ *
+ * @param int $financialTypeID
+ *
+ * @param string $relationshipType
+ *
+ * @return int
+ */
+ public static function getFinancialAccountForFinancialTypeByRelationship($financialTypeID, $relationshipType) {
+ $relationTypeId = key(CRM_Core_PseudoConstant::accountOptionValues('account_relationship', NULL, " AND v.name LIKE '{$relationshipType}' "));
+
+ if (!isset(Civi::$statics[__CLASS__]['entity_financial_account'][$financialTypeID][$relationTypeId])) {
+ $accounts = civicrm_api3('EntityFinancialAccount', 'get', array(
+ 'entity_id' => $financialTypeID,
+ 'entity_table' => 'civicrm_financial_type',
+ ));
+
+ foreach ($accounts['values'] as $account) {
+ Civi::$statics[__CLASS__]['entity_financial_account'][$financialTypeID][$account['account_relationship']] = $account['financial_account_id'];
+ }
+
+ $accountRelationships = CRM_Core_PseudoConstant::accountOptionValues('account_relationship', NULL);
+
+ $incomeAccountRelationshipID = array_search('Income Account is', $accountRelationships);
+ $incomeAccountFinancialAccountID = Civi::$statics[__CLASS__]['entity_financial_account'][$financialTypeID][$incomeAccountRelationshipID];
+
+ foreach (array('Chargeback Account is', 'Credit/Contra Revenue Account is') as $optionalAccountRelationship) {
+
+ $accountRelationshipID = array_search($optionalAccountRelationship, $accountRelationships);
+ if (empty(Civi::$statics[__CLASS__]['entity_financial_account'][$financialTypeID][$accountRelationshipID])) {
+ Civi::$statics[__CLASS__]['entity_financial_account'][$financialTypeID][$accountRelationshipID] = $incomeAccountFinancialAccountID;
+ }
+ }
+
+ }
+ return Civi::$statics[__CLASS__]['entity_financial_account'][$financialTypeID][$relationTypeId];
+ }
+
}
class CRM_Financial_BAO_FinancialAccountTest extends CiviUnitTestCase {
public function setUp() {
+ $this->useTransaction(TRUE);
parent::setUp();
$this->organizationCreate();
}
- public function teardown() {
- $this->financialAccountDelete('Donations');
- }
-
/**
* Check method add()
*/
'is_active' => 1,
);
$ids = $defaults = array();
- $contributionType = CRM_Financial_BAO_FinancialAccount::add($params, $ids);
+ CRM_Financial_BAO_FinancialAccount::add($params);
$result = CRM_Financial_BAO_FinancialAccount::retrieve($params, $defaults);
$this->assertEquals($accountingCode, 4800, 'Verify accounting code.');
}
+ /**
+ * Test getting financial account for a given financial Type with a particular relationship.
+ */
+ public function testGetFinancialAccountByFinancialTypeAndRelationshipBuiltIn() {
+ $this->assertEquals(2, CRM_Financial_BAO_FinancialAccount::getFinancialAccountForFinancialTypeByRelationship(2, 'Income Account Is'));
+ }
+
+ /**
+ * Test getting financial account for a given financial Type with a particular relationship.
+ */
+ public function testGetFinancialAccountByFinancialTypeAndRelationshipBuiltInRefunded() {
+ $this->assertEquals(2, CRM_Financial_BAO_FinancialAccount::getFinancialAccountForFinancialTypeByRelationship(2, 'Credit/Contra Revenue Account Is'));
+ }
+
+ /**
+ * Test getting financial account for a given financial Type with a particular relationship.
+ */
+ public function testGetFinancialAccountByFinancialTypeAndRelationshipBuiltInChargeBack() {
+ $this->assertEquals(2, CRM_Financial_BAO_FinancialAccount::getFinancialAccountForFinancialTypeByRelationship(2, 'Chargeback Account Is'));
+ }
+
+ /**
+ * Test getting financial account for a given financial Type with a particular relationship.
+ */
+ public function testGetFinancialAccountByFinancialTypeAndRelationshipCustomAddedRefunded() {
+ $financialAccount = $this->callAPISuccess('FinancialAccount', 'create', array(
+ 'name' => 'Refund Account',
+ 'is_active' => TRUE,
+ ));
+
+ $this->callAPISuccess('EntityFinancialAccount', 'create', array(
+ 'entity_id' => 2,
+ 'entity_table' => 'civicrm_financial_type',
+ 'account_relationship' => 'Credit/Contra Revenue Account is',
+ 'financial_account_id' => 'Refund Account',
+ ));
+ $this->assertEquals($financialAccount['id'],
+ CRM_Financial_BAO_FinancialAccount::getFinancialAccountForFinancialTypeByRelationship(2, 'Credit/Contra Revenue Account is'));
+ }
+
}
)));
}
+ /**
+ * Refund a contribution for a financial type with a contra account.
+ *
+ * CRM-17951 the contra account is a financial account with a relationship to a
+ * financial type. It is not always configured but should be reflected
+ * in the financial_trxn & financial_item table if it is.
+ */
+ public function testCreateUpdateRefundContributionConfiguredContraAccount() {
+ $financialAccount = $this->callAPISuccess('FinancialAccount', 'create', array(
+ 'name' => 'Refund Account',
+ 'is_active' => TRUE,
+ ));
+
+ $entityFinancialAccount = $this->callAPISuccess('EntityFinancialAccount', 'create', array(
+ 'entity_id' => $this->_financialTypeId,
+ 'entity_table' => 'civicrm_financial_type',
+ 'account_relationship' => 'Credit/Contra Revenue Account is',
+ 'financial_account_id' => 'Refund Account',
+ ));
+
+ $contribution = $this->callAPISuccess('Contribution', 'create', $this->_params);
+ $this->callAPISuccess('Contribution', 'create', array(
+ 'id' => $contribution['id'],
+ 'contribution_status_id' => 'Refunded',
+ ));
+
+ $lineItems = $this->callAPISuccessGetSingle(
+ 'LineItem', array(
+ 'contribution_id' => $contribution['id'],
+ 'api.FinancialItem.getsingle' => array('amount' => array('<' => 0)),
+ ));
+ $this->assertEquals($financialAccount['id'], $lineItems['api.FinancialItem.getsingle']['financial_account_id']);
+
+ $this->callAPISuccess('Contribution', 'delete', array('id' => $contribution['id']));
+ $this->callAPISuccess('EntityFinancialAccount', 'delete', array('id' => $entityFinancialAccount['id']));
+ $this->callAPISuccess('FinancialAccount', 'delete', array('id' => $financialAccount['id']));
+
+ }
+
/**
* Function tests that trxn_id is set when passed in.
*