/** * Process price set and line items. * * @param int $entityId * @param array $lineItem * Line item array. * @param object $contributionDetails * @param string $entityTable * Entity table. * * @param bool $update * * @return void */ public static function processPriceSet($entityId, $lineItem, $contributionDetails = NULL, $entityTable = 'civicrm_contribution', $update = FALSE) { if (!$entityId || !is_array($lineItem) || CRM_Utils_system::isNull($lineItem)) { return; } foreach ($lineItem as $priceSetId => &$values) { if (!$priceSetId) { continue; } foreach ($values as &$line) { $line['entity_table'] = $entityTable; if (empty($line['entity_id'])) { $line['entity_id'] = $entityId; } if (!empty($line['membership_type_id'])) { $line['entity_table'] = 'civicrm_membership'; } if (!empty($contributionDetails->id)) { $line['contribution_id'] = $contributionDetails->id; if ($line['entity_table'] == 'civicrm_contribution') { $line['entity_id'] = $contributionDetails->id; } elseif ($line['entity_table'] == 'civicrm_membership' && !empty($line['entity_id']) && $line['entity_id'] == $contributionDetails->id) { $membershipId = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipPayment', 'contribution_id', $line['entity_id'], 'membership_id'); $line['entity_id'] = $membershipId ? $membershipId : $line['entity_id']; } } // if financial type is not set and if price field value is NOT NULL // get financial type id of price field value if (!empty($line['price_field_value_id']) && empty($line['financial_type_id'])) { $line['financial_type_id'] = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceFieldValue', $line['price_field_value_id'], 'financial_type_id'); } $lineItems = CRM_Price_BAO_LineItem::create($line); if (!$update && $contributionDetails) { $financialItem = CRM_Financial_BAO_FinancialItem::add($lineItems, $contributionDetails); $line['financial_item_id'] = $financialItem->id; if (!empty($line['tax_amount'])) { CRM_Financial_BAO_FinancialItem::add($lineItems, $contributionDetails, TRUE); } } } } if (!$update && $contributionDetails) { CRM_Core_BAO_FinancialTrxn::createDeferredTrxn($lineItem, $contributionDetails); } }
/** * Update all financial accounts entry. * * @param array $params * Contribution object, line item array and params for trxn. * * @param string $context * Update scenarios. * * @param null $skipTrxn * */ public static function updateFinancialAccounts(&$params, $context = NULL, $skipTrxn = NULL) { $itemAmount = $trxnID = NULL; //get all the statuses $contributionStatus = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name'); $previousContributionStatus = CRM_Contribute_PseudoConstant::contributionStatus($params['prevContribution']->contribution_status_id, 'name'); if (($previousContributionStatus == 'Pending' || $previousContributionStatus == 'In Progress') && $params['contribution']->contribution_status_id == array_search('Completed', $contributionStatus) && $context == 'changePaymentInstrument') { return; } if (($previousContributionStatus == 'Partially paid' && $params['contribution']->contribution_status_id == array_search('Completed', $contributionStatus) || $previousContributionStatus == 'Pending' && $params['prevContribution']->is_pay_later == TRUE && $params['contribution']->contribution_status_id == array_search('Partially paid', $contributionStatus)) && $context == 'changedStatus') { return; } if ($context == 'changedAmount' || $context == 'changeFinancialType') { $itemAmount = $params['trxnParams']['total_amount'] = $params['trxnParams']['net_amount'] = $params['total_amount'] - $params['prevContribution']->total_amount; } if ($context == 'changedStatus') { //get all the statuses $contributionStatus = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name'); $cancelledTaxAmount = 0; if ($previousContributionStatus == 'Completed' && self::isContributionStatusNegative($params['contribution']->contribution_status_id)) { $params['trxnParams']['total_amount'] = -$params['total_amount']; $cancelledTaxAmount = CRM_Utils_Array::value('tax_amount', $params, '0.00'); if (empty($params['contribution']->creditnote_id) || $params['contribution']->creditnote_id == "null") { $creditNoteId = self::createCreditNoteId(); CRM_Core_DAO::setFieldValue('CRM_Contribute_DAO_Contribution', $params['contribution']->id, 'creditnote_id', $creditNoteId); } } elseif ($previousContributionStatus == 'Pending' && $params['prevContribution']->is_pay_later || $previousContributionStatus == 'In Progress') { $financialTypeID = CRM_Utils_Array::value('financial_type_id', $params) ? $params['financial_type_id'] : $params['prevContribution']->financial_type_id; $relationTypeId = key(CRM_Core_PseudoConstant::accountOptionValues('account_relationship', NULL, " AND v.name LIKE 'Accounts Receivable Account is' ")); $arAccountId = CRM_Contribute_PseudoConstant::financialAccountType($financialTypeID, $relationTypeId); if ($params['contribution']->contribution_status_id == array_search('Cancelled', $contributionStatus)) { $params['trxnParams']['to_financial_account_id'] = $arAccountId; $params['trxnParams']['total_amount'] = -$params['total_amount']; if (is_null($params['contribution']->creditnote_id) || $params['contribution']->creditnote_id == "null") { $creditNoteId = self::createCreditNoteId(); CRM_Core_DAO::setFieldValue('CRM_Contribute_DAO_Contribution', $params['contribution']->id, 'creditnote_id', $creditNoteId); } } else { $params['trxnParams']['from_financial_account_id'] = $arAccountId; } } $itemAmount = $params['trxnParams']['total_amount'] + $cancelledTaxAmount; } elseif ($context == 'changePaymentInstrument') { $params['trxnParams']['net_amount'] = $params['trxnParams']['total_amount']; $deferredFinancialAccount = CRM_Utils_Array::value('deferred_financial_account_id', $params); if (empty($deferredFinancialAccount)) { $relationTypeId = key(CRM_Core_PseudoConstant::accountOptionValues('account_relationship', NULL, " AND v.name LIKE 'Deferred Revenue Account is' ")); $deferredFinancialAccount = CRM_Contribute_PseudoConstant::financialAccountType($params['prevContribution']->financial_type_id, $relationTypeId); } $lastFinancialTrxnId = CRM_Core_BAO_FinancialTrxn::getFinancialTrxnId($params['prevContribution']->id, 'DESC', FALSE, NULL, $deferredFinancialAccount); if (!empty($lastFinancialTrxnId['financialTrxnId'])) { if ($params['total_amount'] != $params['trxnParams']['total_amount']) { $params['trxnParams']['to_financial_account_id'] = CRM_Core_DAO::getFieldValue('CRM_Financial_DAO_FinancialTrxn', $lastFinancialTrxnId['financialTrxnId'], 'to_financial_account_id'); $params['trxnParams']['payment_instrument_id'] = $params['prevContribution']->payment_instrument_id; } else { $params['trxnParams']['to_financial_account_id'] = $params['to_financial_account_id']; $params['trxnParams']['payment_instrument_id'] = $params['contribution']->payment_instrument_id; } } } if ($context == 'changedStatus') { if (($previousContributionStatus == 'Pending' || $previousContributionStatus == 'In Progress') && $params['contribution']->contribution_status_id == array_search('Completed', $contributionStatus)) { if (empty($params['line_item'])) { //CRM-15296 //@todo - check with Joe regarding this situation - payment processors create pending transactions with no line items // when creating recurring membership payment - there are 2 lines to comment out in contributonPageTest if fixed // & this can be removed return; } self::recordAlwaysAccountsReceivable($params['trxnParams'], $params); $trxn = CRM_Core_BAO_FinancialTrxn::create($params['trxnParams']); $params['entity_id'] = self::$_trxnIDs[] = $trxn->id; $query = "UPDATE civicrm_financial_item SET status_id = %1 WHERE entity_id = %2 and entity_table = 'civicrm_line_item'"; $sql = "SELECT id, amount FROM civicrm_financial_item WHERE entity_id = %1 and entity_table = 'civicrm_line_item'"; $entityParams = array('entity_table' => 'civicrm_financial_item'); foreach ($params['line_item'] as $fieldId => $fields) { foreach ($fields as $fieldValueId => $fieldValues) { $fparams = array(1 => array(CRM_Core_OptionGroup::getValue('financial_item_status', 'Paid', 'name'), 'Integer'), 2 => array($fieldValues['id'], 'Integer')); CRM_Core_DAO::executeQuery($query, $fparams); $fparams = array(1 => array($fieldValues['id'], 'Integer')); $financialItem = CRM_Core_DAO::executeQuery($sql, $fparams); while ($financialItem->fetch()) { $entityParams['entity_id'] = $financialItem->id; $entityParams['amount'] = $financialItem->amount; foreach (self::$_trxnIDs as $tID) { $entityParams['financial_trxn_id'] = $tID; CRM_Financial_BAO_FinancialItem::createEntityTrxn($entityParams); } } } } return; } } $trxn = CRM_Core_BAO_FinancialTrxn::create($params['trxnParams']); $params['entity_id'] = $trxn->id; if ($context != 'changePaymentInstrument') { $itemParams['entity_table'] = 'civicrm_line_item'; $trxnIds['id'] = $params['entity_id']; foreach ($params['line_item'] as $fieldId => $fields) { foreach ($fields as $fieldValueId => $fieldValues) { $prevFinancialItem = CRM_Financial_BAO_FinancialItem::getPreviousFinancialItem($fieldValues['id']); $receiveDate = CRM_Utils_Date::isoToMysql($params['prevContribution']->receive_date); if ($params['contribution']->receive_date) { $receiveDate = CRM_Utils_Date::isoToMysql($params['contribution']->receive_date); } $financialAccount = self::getFinancialAccountForStatusChangeTrxn($params, $prevFinancialItem); $currency = $params['prevContribution']->currency; if ($params['contribution']->currency) { $currency = $params['contribution']->currency; } $diff = 1; if ($context == 'changeFinancialType' || self::isContributionStatusNegative($params['contribution']->contribution_status_id)) { $diff = -1; } if (!empty($params['is_quick_config'])) { $amount = $itemAmount; if (!$amount) { $amount = $params['total_amount']; } } else { $amount = $diff * $fieldValues['line_total']; } $itemParams = array('transaction_date' => $receiveDate, 'contact_id' => $params['prevContribution']->contact_id, 'currency' => $currency, 'amount' => $amount, 'description' => $prevFinancialItem->description, 'status_id' => $prevFinancialItem->status_id, 'financial_account_id' => $financialAccount, 'entity_table' => 'civicrm_line_item', 'entity_id' => $fieldValues['id']); $financialItem = CRM_Financial_BAO_FinancialItem::create($itemParams, NULL, $trxnIds); $params['line_item'][$fieldId][$fieldValueId]['deferred_line_total'] = $amount; $params['line_item'][$fieldId][$fieldValueId]['financial_item_id'] = $financialItem->id; if ($fieldValues['tax_amount']) { $invoiceSettings = Civi::settings()->get('contribution_invoice_settings'); $taxTerm = CRM_Utils_Array::value('tax_term', $invoiceSettings); $itemParams['amount'] = $diff * $fieldValues['tax_amount']; $itemParams['description'] = $taxTerm; if ($fieldValues['financial_type_id']) { $itemParams['financial_account_id'] = self::getFinancialAccountId($fieldValues['financial_type_id']); } CRM_Financial_BAO_FinancialItem::create($itemParams, NULL, $trxnIds); } } } } if ($context == 'changeFinancialType') { $params['skipLineItem'] = FALSE; foreach ($params['line_item'] as &$lineItems) { foreach ($lineItems as &$line) { $line['financial_type_id'] = $params['financial_type_id']; } } } if ($context == 'changePaymentInstrument') { foreach ($params['line_item'] as $lineitems) { foreach ($lineitems as $fieldValueId => $fieldValues) { $prevFinancialItem = CRM_Financial_BAO_FinancialItem::getPreviousFinancialItem($fieldValues['id']); // save to entity_financial_trxn table $entityFinancialTrxnParams = array('entity_table' => "civicrm_financial_item", 'entity_id' => $prevFinancialItem->id, 'financial_trxn_id' => $trxn->id, 'amount' => $trxn->total_amount); civicrm_api3('entityFinancialTrxn', 'create', $entityFinancialTrxnParams); } } } CRM_Core_BAO_FinancialTrxn::createDeferredTrxn(CRM_Utils_Array::value('line_item', $params), $params['contribution'], TRUE, $context); }
/** * Test for createDeferredTrxn(). */ public function testCreateDeferredTrxn() { Civi::settings()->set('contribution_invoice_settings', array('deferred_revenue_enabled' => '1')); $cid = $this->individualCreate(); $params = array('contact_id' => $cid, 'receive_date' => '2016-01-20', 'total_amount' => 622, 'financial_type_id' => 4, 'line_items' => array(array('line_item' => array(array('entity_table' => 'civicrm_contribution', 'price_field_id' => 8, 'price_field_value_id' => 16, 'label' => 'test 1', 'qty' => 1, 'unit_price' => 100, 'line_total' => 100, 'financial_type_id' => 4)), 'params' => array()))); $contribution = CRM_Contribute_BAO_Contribution::create($params); $lineItems[1] = CRM_Price_BAO_LineItem::getLineItemsByContributionID($contribution->id); $lineItemId = key($lineItems[1]); $lineItems[1][$lineItemId]['financial_item_id'] = CRM_Core_DAO::singleValueQuery("SELECT id FROM civicrm_financial_item WHERE entity_table = 'civicrm_line_item' AND entity_id = {$lineItemId}"); // Get financial trxns for contribution $trxn = $this->callAPISuccess("FinancialTrxn", "get", array('total_amount' => 622)); $this->assertEquals(date('Ymd', strtotime($trxn['values'][$trxn['id']]['trxn_date'])), date('Ymd', strtotime('2016-01-20'))); $contribution->revenue_recognition_date = date('Ymd', strtotime("+1 month")); CRM_Core_BAO_FinancialTrxn::createDeferredTrxn($lineItems, $contribution); $trxn = $this->callAPISuccess("FinancialTrxn", "get", array('total_amount' => 622, 'id' => array("NOT IN" => array($trxn['id'])))); $this->assertEquals(date('Ymd', strtotime($trxn['values'][$trxn['id']]['trxn_date'])), date('Ymd', strtotime("+1 month"))); }