From: Eileen McNaughton Date: Sat, 25 Apr 2015 21:52:27 +0000 (-0600) Subject: CRM-16357 - refactor postProcess into a submit function for testability X-Git-Url: https://vcs.fsf.org/?a=commitdiff_plain;h=f75f0921693205a9b9a8a071c40da99f81e5818c;p=civicrm-core.git CRM-16357 - refactor postProcess into a submit function for testability --- diff --git a/CRM/Contribute/Form/Contribution.php b/CRM/Contribute/Form/Contribution.php index 8062df01ae..5f9e3dd1e7 100644 --- a/CRM/Contribute/Form/Contribution.php +++ b/CRM/Contribute/Form/Contribution.php @@ -979,9 +979,6 @@ class CRM_Contribute_Form_Contribution extends CRM_Contribute_Form_AbstractEditP * Process the form submission. */ public function postProcess() { - $sendReceipt = $pId = $contribution = $isRelatedId = FALSE; - $softParams = $softIDs = array(); - if ($this->_action & CRM_Core_Action::DELETE) { CRM_Contribute_BAO_Contribution::deleteContribution($this->_id); CRM_Core_Session::singleton()->replaceUserContext(CRM_Utils_System::url('civicrm/contact/view', @@ -992,894 +989,908 @@ class CRM_Contribute_Form_Contribution extends CRM_Contribute_Form_AbstractEditP // Get the submitted form values. $submittedValues = $this->controller->exportValues($this->_name); - if (!empty($submittedValues['price_set_id']) && $this->_action & CRM_Core_Action::UPDATE) { - $line = CRM_Price_BAO_LineItem::getLineItems($this->_id, 'contribution'); - $lineID = key($line); - $priceSetId = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceField', CRM_Utils_Array::value('price_field_id', $line[$lineID]), 'price_set_id'); - $quickConfig = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceSet', $priceSetId, 'is_quick_config'); - if ($quickConfig) { - CRM_Price_BAO_LineItem::deleteLineItems($this->_id, 'civicrm_contribution'); + $contribution = $this->submit($submittedValues); + $session = CRM_Core_Session::singleton(); + $buttonName = $this->controller->getButtonName(); + if ($this->_context == 'standalone') { + if ($buttonName == $this->getButtonName('upload', 'new')) { + $session->replaceUserContext(CRM_Utils_System::url('civicrm/contribute/add', + 'reset=1&action=add&context=standalone' + )); } + else { + $session->replaceUserContext(CRM_Utils_System::url('civicrm/contact/view', + "reset=1&cid={$this->_contactID}&selectedChild=contribute" + )); + } + } + elseif ($this->_context == 'contribution' && $this->_mode && $buttonName == $this->getButtonName('upload', 'new')) { + $session->replaceUserContext(CRM_Utils_System::url('civicrm/contact/view/contribution', + "reset=1&action=add&context={$this->_context}&cid={$this->_contactID}&mode={$this->_mode}" + )); + } + elseif ($buttonName == $this->getButtonName('upload', 'new')) { + $session->replaceUserContext(CRM_Utils_System::url('civicrm/contact/view/contribution', + "reset=1&action=add&context={$this->_context}&cid={$this->_contactID}" + )); } - // Process price set and get total amount and line items. - $lineItem = array(); - $priceSetId = CRM_Utils_Array::value('price_set_id', $submittedValues); - if (empty($priceSetId) && !$this->_id) { - $this->_priceSetId = $priceSetId = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceSet', 'default_contribution_amount', 'id', 'name'); - $this->_priceSet = current(CRM_Price_BAO_PriceSet::getSetDetail($priceSetId)); - $fieldID = key($this->_priceSet['fields']); - $fieldValueId = key($this->_priceSet['fields'][$fieldID]['options']); - $this->_priceSet['fields'][$fieldID]['options'][$fieldValueId]['amount'] = $submittedValues['total_amount']; - $submittedValues['price_' . $fieldID] = 1; + //store contribution ID if not yet set (on create) + if (empty($this->_id) && !empty($contribution->id)) { + $this->_id = $contribution->id; } + } - if ($priceSetId) { - CRM_Price_BAO_PriceSet::processAmount($this->_priceSet['fields'], - $submittedValues, $lineItem[$priceSetId]); + /** + * Process credit card payment. + * + * @param array $submittedValues + * @param array $lineItem + * + * @throws CRM_Core_Exception + */ + protected function processCreditCard($submittedValues, $lineItem) { + $sendReceipt = $contribution = FALSE; - // Unset tax amount for offline 'is_quick_config' contribution. - if ($this->_priceSet['is_quick_config'] && - !array_key_exists($submittedValues['financial_type_id'], CRM_Core_PseudoConstant::getTaxRates()) - ) { - unset($submittedValues['tax_amount']); + $unsetParams = array( + 'trxn_id', + 'payment_instrument_id', + 'contribution_status_id', + 'cancel_date', + 'cancel_reason', + ); + foreach ($unsetParams as $key) { + if (isset($submittedValues[$key])) { + unset($submittedValues[$key]); } - $submittedValues['total_amount'] = CRM_Utils_Array::value('amount', $submittedValues); } - if ($this->_id) { - if ($this->_compId) { - if ($this->_context == 'participant') { - $pId = $this->_compId; - } - elseif ($this->_context == 'membership') { - $isRelatedId = TRUE; - } - else { - $pId = CRM_Core_DAO::getFieldValue('CRM_Event_DAO_ParticipantPayment', $this->_id, 'participant_id', 'contribution_id'); - } - } - else { - $contributionDetails = CRM_Contribute_BAO_Contribution::getComponentDetails($this->_id); - if (array_key_exists('membership', $contributionDetails)) { - $isRelatedId = TRUE; - } - elseif (array_key_exists('participant', $contributionDetails)) { - $pId = $contributionDetails['participant']; - } - } + $isTest = ($this->_mode == 'test') ? 1 : 0; + // CRM-12680 set $_lineItem if its not set + if (empty($this->_lineItem) && !empty($lineItem)) { + $this->_lineItem = $lineItem; } - if (!$priceSetId && !empty($submittedValues['total_amount']) && $this->_id) { - // CRM-10117 update the line items for participants. - if ($pId) { - $entityTable = 'participant'; - $entityID = $pId; - $isRelatedId = FALSE; - $participantParams = array( - 'fee_amount' => $submittedValues['total_amount'], - 'id' => $entityID, - ); - CRM_Event_BAO_Participant::add($participantParams); - if (empty($this->_lineItems)) { - $this->_lineItems[] = CRM_Price_BAO_LineItem::getLineItems($entityID, 'participant', 1); - } - } - else { - $entityTable = 'contribution'; - $entityID = $this->_id; - } - $lineItems = CRM_Price_BAO_LineItem::getLineItems($entityID, $entityTable, NULL, TRUE, $isRelatedId); - foreach (array_keys($lineItems) as $id) { - $lineItems[$id]['id'] = $id; - } - $itemId = key($lineItems); - if ($itemId && !empty($lineItems[$itemId]['price_field_id'])) { - $this->_priceSetId = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceField', $lineItems[$itemId]['price_field_id'], 'price_set_id'); - } + //Get the require fields value only. + $params = $this->_params = $submittedValues; - if ($this->_priceSetId && CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceSet', $this->_priceSetId, 'is_quick_config')) { - $lineItems[$itemId]['unit_price'] = $lineItems[$itemId]['line_total'] = CRM_Utils_Rule::cleanMoney(CRM_Utils_Array::value('total_amount', $submittedValues)); + $this->_paymentProcessor = CRM_Financial_BAO_PaymentProcessor::getPayment($this->_params['payment_processor_id'], + $this->_mode + ); - // Update line total and total amount with tax on edit. - $financialItemsId = CRM_Core_PseudoConstant::getTaxRates(); - if (array_key_exists($submittedValues['financial_type_id'], $financialItemsId)) { - $lineItems[$itemId]['tax_rate'] = $financialItemsId[$submittedValues['financial_type_id']]; - } - else { - $lineItems[$itemId]['tax_rate'] = $lineItems[$itemId]['tax_amount'] = ""; - $submittedValues['tax_amount'] = 'null'; - } - if ($lineItems[$itemId]['tax_rate']) { - $lineItems[$itemId]['tax_amount'] = ($lineItems[$itemId]['tax_rate'] / 100) * $lineItems[$itemId]['line_total']; - $submittedValues['total_amount'] = $lineItems[$itemId]['line_total'] + $lineItems[$itemId]['tax_amount']; - $submittedValues['tax_amount'] = $lineItems[$itemId]['tax_amount']; - } - } - // CRM-10117 update the line items for participants. - if (!empty($lineItems[$itemId]['price_field_id'])) { - $lineItem[$this->_priceSetId] = $lineItems; - } - } + // Get the payment processor id as per mode. + $this->_params['payment_processor'] = $params['payment_processor_id'] + = $this->_params['payment_processor_id'] = $submittedValues['payment_processor_id'] = $this->_paymentProcessor['id']; - $isQuickConfig = 0; - if ($this->_priceSetId && CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceSet', $this->_priceSetId, 'is_quick_config')) { - $isQuickConfig = 1; - } - //CRM-11529 for quick config back office transactions - //when financial_type_id is passed in form, update the - //line items with the financial type selected in form - if ($isQuickConfig && !empty($submittedValues['financial_type_id']) && CRM_Utils_Array::value($this->_priceSetId, $lineItem) - ) { - foreach ($lineItem[$this->_priceSetId] as &$values) { - $values['financial_type_id'] = $submittedValues['financial_type_id']; - } - } + $now = date('YmdHis'); + $fields = array(); - if (!isset($submittedValues['total_amount'])) { - $submittedValues['total_amount'] = CRM_Utils_Array::value('total_amount', $this->_values); + // we need to retrieve email address + if ($this->_context == 'standalone' && !empty($submittedValues['is_email_receipt'])) { + list($this->userDisplayName, + $this->userEmail + ) = CRM_Contact_BAO_Contact_Location::getEmailDetails($this->_contactID); + $this->assign('displayName', $this->userDisplayName); } - $this->assign('lineItem', !empty($lineItem) && !$isQuickConfig ? $lineItem : FALSE); - if (!empty($submittedValues['pcp_made_through_id'])) { - $pcp = array(); - $fields = array( - 'pcp_made_through_id', - 'pcp_display_in_roll', - 'pcp_roll_nickname', - 'pcp_personal_note', - ); - foreach ($fields as $f) { - $pcp[$f] = CRM_Utils_Array::value($f, $submittedValues); - } - } + // Set email for primary location. + $fields['email-Primary'] = 1; + $params['email-Primary'] = $this->userEmail; - $isEmpty = array_keys(array_flip($submittedValues['soft_credit_contact_id'])); - if ($this->_id && count($isEmpty) == 1 && key($isEmpty) == NULL) { - //Delete existing soft credit records if soft credit list is empty on update - CRM_Contribute_BAO_ContributionSoft::del(array('contribution_id' => $this->_id)); + // now set the values for the billing location. + foreach (array_keys($this->_fields) as $name) { + $fields[$name] = 1; } - else { - //build soft credit params - foreach ($submittedValues['soft_credit_contact_id'] as $key => $val) { - if ($val && $submittedValues['soft_credit_amount'][$key]) { - $softParams[$key]['contact_id'] = $val; - $softParams[$key]['amount'] = CRM_Utils_Rule::cleanMoney($submittedValues['soft_credit_amount'][$key]); - $softParams[$key]['soft_credit_type_id'] = $submittedValues['soft_credit_type'][$key]; - if (!empty($submittedValues['soft_credit_id'][$key])) { - $softIDs[] = $softParams[$key]['id'] = $submittedValues['soft_credit_id'][$key]; - } - } + + // also add location name to the array + $params["address_name-{$this->_bltID}"] = CRM_Utils_Array::value('billing_first_name', $params) . ' ' . CRM_Utils_Array::value('billing_middle_name', $params) . ' ' . CRM_Utils_Array::value('billing_last_name', $params); + + $params["address_name-{$this->_bltID}"] = trim($params["address_name-{$this->_bltID}"]); + $fields["address_name-{$this->_bltID}"] = 1; + $ctype = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', + $this->_contactID, + 'contact_type' + ); + + $nameFields = array('first_name', 'middle_name', 'last_name'); + foreach ($nameFields as $name) { + $fields[$name] = 1; + if (array_key_exists("billing_$name", $params)) { + $params[$name] = $params["billing_{$name}"]; + $params['preserveDBName'] = TRUE; } } - // set the contact, when contact is selected - if (!empty($submittedValues['contact_id'])) { - $this->_contactID = $submittedValues['contact_id']; + if (!empty($params['source'])) { + unset($params['source']); } + $contactID = CRM_Contact_BAO_Contact::createProfileContact($params, $fields, + $this->_contactID, + NULL, NULL, + $ctype + ); - // Credit Card Contribution. - if ($this->_mode) { - $this->processCreditCard($submittedValues, $lineItem); + // add all the additional payment params we need + if (!empty($this->_params["billing_state_province_id-{$this->_bltID}"])) { + $this->_params["state_province-{$this->_bltID}"] = $this->_params["billing_state_province-{$this->_bltID}"] = CRM_Core_PseudoConstant::stateProvinceAbbreviation($this->_params["billing_state_province_id-{$this->_bltID}"]); + } + if (!empty($this->_params["billing_country_id-{$this->_bltID}"])) { + $this->_params["country-{$this->_bltID}"] = $this->_params["billing_country-{$this->_bltID}"] = CRM_Core_PseudoConstant::countryIsoCode($this->_params["billing_country_id-{$this->_bltID}"]); + } + $legacyCreditCardExpiryCheck = FALSE; + if ($this->_paymentProcessor['payment_type'] & CRM_Core_Payment::PAYMENT_TYPE_CREDIT_CARD && !isset($this->_paymentFields)) { + $legacyCreditCardExpiryCheck = TRUE; + } + if ($legacyCreditCardExpiryCheck || in_array('credit_card_exp_date', array_keys($this->_paymentFields))) { + $this->_params['year'] = CRM_Core_Payment_Form::getCreditCardExpirationYear($this->_params); + $this->_params['month'] = CRM_Core_Payment_Form::getCreditCardExpirationMonth($this->_params); + } + $this->_params['ip_address'] = CRM_Utils_System::ipAddress(); + $this->_params['amount'] = $this->_params['total_amount']; + $this->_params['amount_level'] = 0; + $this->_params['description'] = ts('Office Credit Card contribution'); + $this->_params['currencyID'] = CRM_Utils_Array::value('currency', + $this->_params, + CRM_Core_Config::singleton()->defaultCurrency + ); + $this->_params['payment_action'] = 'Sale'; + if (!empty($this->_params['receive_date'])) { + $this->_params['receive_date'] = CRM_Utils_Date::processDate($this->_params['receive_date'], $this->_params['receive_date_time']); } - else { - // Offline Contribution. - $submittedValues = $this->unsetCreditCardFields($submittedValues); - - // get the required field value only. - $formValues = $submittedValues; - $params = $ids = array(); - - $params['contact_id'] = $this->_contactID; - - $params['currency'] = $this->getCurrency($submittedValues); - $fields = array( - 'financial_type_id', - 'contribution_status_id', - 'payment_instrument_id', - 'cancel_reason', - 'source', - 'check_number', - ); - foreach ($fields as $f) { - $params[$f] = CRM_Utils_Array::value($f, $formValues); - } + if (!empty($params['soft_credit_to'])) { + $this->_params['soft_credit_to'] = $params['soft_credit_to']; + $this->_params['pcp_made_through_id'] = $params['pcp_made_through_id']; + } - if (!empty($pcp)) { - $params['pcp'] = $pcp; - } - if (!empty($softParams)) { - $params['soft_credit'] = $softParams; - $params['soft_credit_ids'] = $softIDs; - } + $this->_params['pcp_display_in_roll'] = CRM_Utils_Array::value('pcp_display_in_roll', $params); + $this->_params['pcp_roll_nickname'] = CRM_Utils_Array::value('pcp_roll_nickname', $params); + $this->_params['pcp_personal_note'] = CRM_Utils_Array::value('pcp_personal_note', $params); - // CRM-5740 if priceset is used, no need to cleanup money. - if ($priceSetId) { - $params['skipCleanMoney'] = 1; - } + //Add common data to formatted params + CRM_Contribute_Form_AdditionalInfo::postProcessCommon($params, $this->_params, $this); - $dates = array( - 'receive_date', - 'receipt_date', - 'cancel_date', - ); + if (empty($this->_params['invoice_id'])) { + $this->_params['invoiceID'] = md5(uniqid(rand(), TRUE)); + } + else { + $this->_params['invoiceID'] = $this->_params['invoice_id']; + } - foreach ($dates as $d) { - $params[$d] = CRM_Utils_Date::processDate($formValues[$d], $formValues[$d . '_time'], TRUE); - } + // At this point we've created a contact and stored its address etc + // all the payment processors expect the name and address to be in the + // so we copy stuff over to first_name etc. + $paymentParams = $this->_params; + $paymentParams['contactID'] = $this->_contactID; + CRM_Core_Payment_Form::mapParams($this->_bltID, $this->_params, $paymentParams, TRUE); - if (!empty($formValues['is_email_receipt'])) { - $params['receipt_date'] = date("Y-m-d"); - } + $contributionType = new CRM_Financial_DAO_FinancialType(); + $contributionType->id = $params['financial_type_id']; - if ($params['contribution_status_id'] == CRM_Core_OptionGroup::getValue('contribution_status', 'Cancelled', 'name') - || $params['contribution_status_id'] == CRM_Core_OptionGroup::getValue('contribution_status', 'Refunded', 'name') - ) { - if (CRM_Utils_System::isNull(CRM_Utils_Array::value('cancel_date', $params))) { - $params['cancel_date'] = date('Y-m-d'); - } - } - else { - $params['cancel_date'] = $params['cancel_reason'] = 'null'; - } + // Add some financial type details to the params list + // if folks need to use it. + $paymentParams['contributionType_name'] = $this->_params['contributionType_name'] = $contributionType->name; + $paymentParams['contributionPageID'] = NULL; - // Set is_pay_later flag for back-office offline Pending status contributions CRM-8996 - // else if contribution_status is changed to Completed is_pay_later flag is changed to 0, CRM-15041 - if ($params['contribution_status_id'] == CRM_Core_OptionGroup::getValue('contribution_status', 'Pending', 'name')) { - $params['is_pay_later'] = 1; - } - elseif ($params['contribution_status_id'] == CRM_Core_OptionGroup::getValue('contribution_status', 'Completed', 'name')) { - $params['is_pay_later'] = 0; - } + if (!empty($this->_params['is_email_receipt'])) { + $paymentParams['email'] = $this->userEmail; + $paymentParams['is_email_receipt'] = 1; + } + else { + $paymentParams['is_email_receipt'] = 0; + $this->_params['is_email_receipt'] = 0; + } + if (!empty($this->_params['receive_date'])) { + $paymentParams['receive_date'] = $this->_params['receive_date']; + } - $ids['contribution'] = $params['id'] = $this->_id; + // For recurring contribution, create Contribution Record first. + // Contribution ID, Recurring ID and Contact ID needed + // When we get a callback from the payment processor, CRM-7115 - // Add Additional common information to formatted params. - CRM_Contribute_Form_AdditionalInfo::postProcessCommon($formValues, $params, $this); - if ($pId) { - $params['contribution_mode'] = 'participant'; - $params['participant_id'] = $pId; - $params['skipLineItem'] = 1; - } - elseif ($isRelatedId) { - $params['contribution_mode'] = 'membership'; - } - $params['line_item'] = $lineItem; - $params['payment_processor_id'] = $params['payment_processor'] = CRM_Utils_Array::value('id', $this->_paymentProcessor); - if (isset($submittedValues['tax_amount'])) { - $params['tax_amount'] = $submittedValues['tax_amount']; - } - //create contribution. - if ($isQuickConfig) { - $params['is_quick_config'] = 1; + if (!empty($paymentParams['is_recur'])) { + $contribution = CRM_Contribute_Form_Contribution_Confirm::processContribution($this, + $this->_params, + NULL, + $this->_contactID, + $contributionType, + TRUE, + FALSE, + $isTest, + $this->_lineItem + ); + $paymentParams['contributionID'] = $contribution->id; + $paymentParams['contributionTypeID'] = $contribution->financial_type_id; + $paymentParams['contributionPageID'] = $contribution->contribution_page_id; + $paymentParams['contributionRecurID'] = $contribution->contribution_recur_id; + } + $result = array(); + if ($paymentParams['amount'] > 0.0) { + // force a re-get of the payment processor in case the form changed it, CRM-7179 + $payment = CRM_Core_Payment::singleton($this->_mode, $this->_paymentProcessor, $this, TRUE); + try { + $result = $payment->doPayment($paymentParams, 'contribute'); } - - // CRM-11956 - // if non_deductible_amount exists i.e. Additional Details field set was opened [and staff typed something] - - // if non_deductible_amount does NOT exist - then calculate it depending on: - // $ContributionType->is_deductible and whether there is a product (premium). - if (empty($params['non_deductible_amount'])) { - $contributionType = new CRM_Financial_DAO_FinancialType(); - $contributionType->id = $params['financial_type_id']; - if (!$contributionType->find(TRUE)) { - CRM_Core_Error::fatal('Could not find a system table'); - } - if ($contributionType->is_deductible) { - - if (isset($formValues['product_name'][0])) { - $selectProduct = $formValues['product_name'][0]; - } - // if there is a product - compare the value to the contribution amount - if (isset($selectProduct)) { - $productDAO = new CRM_Contribute_DAO_Product(); - $productDAO->id = $selectProduct; - $productDAO->find(TRUE); - // product value exceeds contribution amount - if ($params['total_amount'] < $productDAO->price) { - $params['non_deductible_amount'] = $params['total_amount']; - } - // product value does NOT exceed contribution amount - else { - $params['non_deductible_amount'] = $productDAO->price; - } - } - // contribution is deductible - but there is no product - else { - $params['non_deductible_amount'] = '0.00'; - } + catch (CRM_Core_Exception $e) { + $message = ts("Payment Processor Error message") . $e->getMessage(); + $this->cleanupDBAfterPaymentFailure($paymentParams, $message); + // Set the contribution mode. + $urlParams = "action=add&cid={$this->_contactID}"; + if ($this->_mode) { + $urlParams .= "&mode={$this->_mode}"; } - // contribution is NOT deductible - else { - $params['non_deductible_amount'] = $params['total_amount']; + if (!empty($this->_ppID)) { + $urlParams .= "&context=pledge&ppid={$this->_ppID}"; } + CRM_Core_Error::statusBounce($message, $urlParams, ts('Payment Processor Error')); } + } - $contribution = CRM_Contribute_BAO_Contribution::create($params, $ids); + $this->_params = array_merge($this->_params, $result); - // process associated membership / participant, CRM-4395 - $relatedComponentStatusMsg = NULL; - if ($contribution->id && $this->_action & CRM_Core_Action::UPDATE) { - $relatedComponentStatusMsg = $this->updateRelatedComponent($contribution->id, - $contribution->contribution_status_id, - CRM_Utils_Array::value('contribution_status_id', - $this->_values - ), - $contribution->receive_date - ); - } + $this->_params['receive_date'] = $now; - //process note - if ($contribution->id && isset($formValues['note'])) { - CRM_Contribute_Form_AdditionalInfo::processNote($formValues, $this->_contactID, $contribution->id, $this->_noteID); - } + if (!empty($this->_params['is_email_receipt'])) { + $this->_params['receipt_date'] = $now; + } + else { + $this->_params['receipt_date'] = CRM_Utils_Date::processDate($this->_params['receipt_date'], + $params['receipt_date_time'], TRUE + ); + } - //process premium - if ($contribution->id && isset($formValues['product_name'][0])) { - CRM_Contribute_Form_AdditionalInfo::processPremium($formValues, $contribution->id, - $this->_premiumID, $this->_options - ); - } + $this->set('params', $this->_params); + $this->assign('trxn_id', $result['trxn_id']); + $this->assign('receive_date', $this->_params['receive_date']); - // assign tax calculation for contribution receipts - $taxRate = array(); - $getTaxDetails = FALSE; - $invoiceSettings = CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::CONTRIBUTE_PREFERENCES_NAME, 'contribution_invoice_settings'); - $invoicing = CRM_Utils_Array::value('invoicing', $invoiceSettings); - if ($invoicing) { - if ($this->_action & CRM_Core_Action::ADD) { - $line = $lineItem; - } - elseif ($this->_action & CRM_Core_Action::UPDATE) { - $line = $this->_lineItems; - } - foreach ($line as $key => $value) { - foreach ($value as $v) { - if (isset($taxRate[(string) CRM_Utils_Array::value('tax_rate', $v)])) { - $taxRate[(string) $v['tax_rate']] = $taxRate[(string) $v['tax_rate']] + CRM_Utils_Array::value('tax_amount', $v); - } - else { - if (isset($v['tax_rate'])) { - $taxRate[(string) $v['tax_rate']] = CRM_Utils_Array::value('tax_amount', $v); - $getTaxDetails = TRUE; - } - } - } - } - } - - if ($invoicing) { - if ($this->_action & CRM_Core_Action::UPDATE) { - if (isset($submittedValues['tax_amount'])) { - $totalTaxAmount = $submittedValues['tax_amount']; - } - else { - $totalTaxAmount = $this->_values['tax_amount']; - } - $this->assign('totalTaxAmount', $totalTaxAmount); - $this->assign('dataArray', $taxRate); - } - else { - if (!empty($submittedValues['price_set_id'])) { - $this->assign('totalTaxAmount', $submittedValues['tax_amount']); - $this->assign('getTaxDetails', $getTaxDetails); - $this->assign('dataArray', $taxRate); - $this->assign('taxTerm', CRM_Utils_Array::value('tax_term', $invoiceSettings)); - } - else { - $this->assign('totalTaxAmount', CRM_Utils_Array::value('tax_amount', $submittedValues)); - } - } - } - - //send receipt mail. - if ($contribution->id && !empty($formValues['is_email_receipt'])) { - $formValues['contact_id'] = $this->_contactID; - $formValues['contribution_id'] = $contribution->id; - - $formValues += CRM_Contribute_BAO_ContributionSoft::getSoftContribution($contribution->id); - - // to get 'from email id' for send receipt - $this->fromEmailId = $formValues['from_email_address']; - $sendReceipt = CRM_Contribute_Form_AdditionalInfo::emailReceipt($this, $formValues); - } + // Result has all the stuff we need + // lets archive it to a financial transaction + if ($contributionType->is_deductible) { + $this->assign('is_deductible', TRUE); + $this->set('is_deductible', TRUE); + } - $pledgePaymentId = CRM_Core_DAO::getFieldValue('CRM_Pledge_DAO_PledgePayment', - $contribution->id, - 'id', - 'contribution_id' + // Set source if not set + if (empty($this->_params['source'])) { + $userID = CRM_Core_Session::singleton()->get('userID'); + $userSortName = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $userID, + 'sort_name' ); + $this->_params['source'] = ts('Submit Credit Card Payment by: %1', array(1 => $userSortName)); + } - //update pledge payment status. - if ((($this->_ppID && $contribution->id) && $this->_action & CRM_Core_Action::ADD) || - (($pledgePaymentId) && $this->_action & CRM_Core_Action::UPDATE) - ) { + // Build custom data getFields array + $customFieldsContributionType = CRM_Core_BAO_CustomField::getFields('Contribution', FALSE, FALSE, + CRM_Utils_Array::value('financial_type_id', $params) + ); + $customFields = CRM_Utils_Array::crmArrayMerge($customFieldsContributionType, + CRM_Core_BAO_CustomField::getFields('Contribution', FALSE, FALSE, NULL, NULL, TRUE) + ); + $params['custom'] = CRM_Core_BAO_CustomField::postProcess($params, + $customFields, + $this->_id, + 'Contribution' + ); + if (empty($paymentParams['is_recur'])) { + $contribution = CRM_Contribute_Form_Contribution_Confirm::processContribution($this, + $this->_params, + $result, + $this->_contactID, + $contributionType, + FALSE, FALSE, + $isTest, + $this->_lineItem + ); + } - if ($this->_ppID) { - //store contribution id in payment record. - CRM_Core_DAO::setFieldValue('CRM_Pledge_DAO_PledgePayment', $this->_ppID, 'contribution_id', $contribution->id); - } - else { - $this->_ppID = CRM_Core_DAO::getFieldValue('CRM_Pledge_DAO_PledgePayment', - $contribution->id, - 'id', - 'contribution_id' - ); - $this->_pledgeID = CRM_Core_DAO::getFieldValue('CRM_Pledge_DAO_PledgePayment', - $contribution->id, - 'pledge_id', - 'contribution_id' - ); - } + // Send receipt mail. + if ($contribution->id && !empty($this->_params['is_email_receipt'])) { + $this->_params['trxn_id'] = CRM_Utils_Array::value('trxn_id', $result); + $this->_params['contact_id'] = $this->_contactID; + $this->_params['contribution_id'] = $contribution->id; + $sendReceipt = CRM_Contribute_Form_AdditionalInfo::emailReceipt($this, $this->_params, TRUE); + } - $adjustTotalAmount = FALSE; - if (CRM_Utils_Array::value('option_type', $formValues) == 2) { - $adjustTotalAmount = TRUE; - } + //process the note + if ($contribution->id && isset($params['note'])) { + CRM_Contribute_Form_AdditionalInfo::processNote($params, $contactID, $contribution->id, NULL); + } + //process premium + if ($contribution->id && isset($params['product_name'][0])) { + CRM_Contribute_Form_AdditionalInfo::processPremium($params, $contribution->id, NULL, $this->_options); + } - $updatePledgePaymentStatus = FALSE; - //do only if either the status or the amount has changed - if ($this->_action & CRM_Core_Action::ADD) { - $updatePledgePaymentStatus = TRUE; - } - elseif ($this->_action & CRM_Core_Action::UPDATE && (($this->_defaults['contribution_status_id'] != $formValues['contribution_status_id']) || - ($this->_defaults['total_amount'] != $formValues['total_amount'])) - ) { - $updatePledgePaymentStatus = TRUE; - } + //update pledge payment status. + if ($this->_ppID && $contribution->id) { + // Store contribution id in payment record. + CRM_Core_DAO::setFieldValue('CRM_Pledge_DAO_PledgePayment', $this->_ppID, 'contribution_id', $contribution->id); - if ($updatePledgePaymentStatus) { - CRM_Pledge_BAO_PledgePayment::updatePledgePaymentStatus($this->_pledgeID, - array($this->_ppID), - $contribution->contribution_status_id, - NULL, - $contribution->total_amount, - $adjustTotalAmount - ); - } - } + CRM_Pledge_BAO_PledgePayment::updatePledgePaymentStatus($this->_pledgeID, + array($this->_ppID), + $contribution->contribution_status_id, + NULL, + $contribution->total_amount + ); + } - $statusMsg = ts('The contribution record has been saved.'); - if (!empty($formValues['is_email_receipt']) && $sendReceipt) { + if ($contribution->id) { + $statusMsg = ts('The contribution record has been processed.'); + if (!empty($this->_params['is_email_receipt']) && $sendReceipt) { $statusMsg .= ' ' . ts('A receipt has been emailed to the contributor.'); } - - if ($relatedComponentStatusMsg) { - $statusMsg .= ' ' . $relatedComponentStatusMsg; - } - - CRM_Core_Session::setStatus($statusMsg, ts('Saved'), 'success'); - //Offline Contribution ends. - } - $session = CRM_Core_Session::singleton(); - $buttonName = $this->controller->getButtonName(); - if ($this->_context == 'standalone') { - if ($buttonName == $this->getButtonName('upload', 'new')) { - $session->replaceUserContext(CRM_Utils_System::url('civicrm/contribute/add', - 'reset=1&action=add&context=standalone' - )); - } - else { - $session->replaceUserContext(CRM_Utils_System::url('civicrm/contact/view', - "reset=1&cid={$this->_contactID}&selectedChild=contribute" - )); - } - } - elseif ($this->_context == 'contribution' && $this->_mode && $buttonName == $this->getButtonName('upload', 'new')) { - $session->replaceUserContext(CRM_Utils_System::url('civicrm/contact/view/contribution', - "reset=1&action=add&context={$this->_context}&cid={$this->_contactID}&mode={$this->_mode}" - )); - } - elseif ($buttonName == $this->getButtonName('upload', 'new')) { - $session->replaceUserContext(CRM_Utils_System::url('civicrm/contact/view/contribution', - "reset=1&action=add&context={$this->_context}&cid={$this->_contactID}" - )); - } - - //store contribution ID if not yet set (on create) - if (empty($this->_id) && !empty($contribution->id)) { - $this->_id = $contribution->id; + CRM_Core_Session::setStatus($statusMsg, ts('Complete'), 'success'); } } /** - * Process credit card payment. + * Clean up DB after payment fails. * - * @param array $submittedValues - * @param array $lineItem + * This function removes related DB entries. Note that it has been agreed in principle, + * but not implemented, that contributions should be retained as 'Failed' rather than + * deleted. * - * @throws CRM_Core_Exception + * @todo it doesn't clean up line items. + * + * @param array $paymentParams + * @param string $message */ - protected function processCreditCard($submittedValues, $lineItem) { - $sendReceipt = $contribution = FALSE; - - $unsetParams = array( - 'trxn_id', - 'payment_instrument_id', - 'contribution_status_id', - 'cancel_date', - 'cancel_reason', - ); - foreach ($unsetParams as $key) { - if (isset($submittedValues[$key])) { - unset($submittedValues[$key]); - } + public function cleanupDBAfterPaymentFailure($paymentParams, $message) { + // Make sure to cleanup db for recurring case. + if (!empty($paymentParams['contributionID'])) { + CRM_Core_Error::debug_log_message($message . + "contact id={$this->_contactID} (deleting contribution {$paymentParams['contributionID']}"); + CRM_Contribute_BAO_Contribution::deleteContribution($paymentParams['contributionID']); } - $isTest = ($this->_mode == 'test') ? 1 : 0; - // CRM-12680 set $_lineItem if its not set - if (empty($this->_lineItem) && !empty($lineItem)) { - $this->_lineItem = $lineItem; + if (!empty($paymentParams['contributionRecurID'])) { + CRM_Core_Error::debug_log_message($message . + "contact id={$this->_contactID} (deleting recurring contribution {$paymentParams['contributionRecurID']}"); + CRM_Contribute_BAO_ContributionRecur::deleteRecurContribution($paymentParams['contributionRecurID']); } + } - //Get the require fields value only. - $params = $this->_params = $submittedValues; - - $this->_paymentProcessor = CRM_Financial_BAO_PaymentProcessor::getPayment($this->_params['payment_processor_id'], - $this->_mode - ); - - // Get the payment processor id as per mode. - $this->_params['payment_processor'] = $params['payment_processor_id'] - = $this->_params['payment_processor_id'] = $submittedValues['payment_processor_id'] = $this->_paymentProcessor['id']; - - $now = date('YmdHis'); - $fields = array(); - - // we need to retrieve email address - if ($this->_context == 'standalone' && !empty($submittedValues['is_email_receipt'])) { - list($this->userDisplayName, - $this->userEmail - ) = CRM_Contact_BAO_Contact_Location::getEmailDetails($this->_contactID); - $this->assign('displayName', $this->userDisplayName); - } - - // Set email for primary location. - $fields['email-Primary'] = 1; - $params['email-Primary'] = $this->userEmail; - - // now set the values for the billing location. - foreach (array_keys($this->_fields) as $name) { - $fields[$name] = 1; - } - - // also add location name to the array - $params["address_name-{$this->_bltID}"] = CRM_Utils_Array::value('billing_first_name', $params) . ' ' . CRM_Utils_Array::value('billing_middle_name', $params) . ' ' . CRM_Utils_Array::value('billing_last_name', $params); - - $params["address_name-{$this->_bltID}"] = trim($params["address_name-{$this->_bltID}"]); - $fields["address_name-{$this->_bltID}"] = 1; - $ctype = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', - $this->_contactID, - 'contact_type' - ); - - $nameFields = array('first_name', 'middle_name', 'last_name'); - foreach ($nameFields as $name) { - $fields[$name] = 1; - if (array_key_exists("billing_$name", $params)) { - $params[$name] = $params["billing_{$name}"]; - $params['preserveDBName'] = TRUE; - } + /** + * Generate the data to construct a snippet based pane. + * + * This form also assigns the showAdditionalInfo var based on historical code. + * This appears to mean 'there is a pane to show'. + * + * @param string $type + * Type of Pane - this is generally used to determine the function name used to build it + * - e.g CreditCard, AdditionalDetail + * @param array $defaults + * + * @return array + * We aim to further refactor & simplify this but currently + * - the panes array + * - should additional info be shown? + */ + protected function generatePane($type, $defaults) { + $urlParams = "snippet=4&formType={$type}"; + if ($this->_mode) { + $urlParams .= "&mode={$this->_mode}"; } - if (!empty($params['source'])) { - unset($params['source']); + $open = 'false'; + if ($type == 'CreditCard' || + $type == 'DirectDebit' + ) { + $open = 'true'; } - $contactID = CRM_Contact_BAO_Contact::createProfileContact($params, $fields, - $this->_contactID, - NULL, NULL, - $ctype - ); - // add all the additional payment params we need - if (!empty($this->_params["billing_state_province_id-{$this->_bltID}"])) { - $this->_params["state_province-{$this->_bltID}"] = $this->_params["billing_state_province-{$this->_bltID}"] = CRM_Core_PseudoConstant::stateProvinceAbbreviation($this->_params["billing_state_province_id-{$this->_bltID}"]); - } - if (!empty($this->_params["billing_country_id-{$this->_bltID}"])) { - $this->_params["country-{$this->_bltID}"] = $this->_params["billing_country-{$this->_bltID}"] = CRM_Core_PseudoConstant::countryIsoCode($this->_params["billing_country_id-{$this->_bltID}"]); - } - $legacyCreditCardExpiryCheck = FALSE; - if ($this->_paymentProcessor['payment_type'] & CRM_Core_Payment::PAYMENT_TYPE_CREDIT_CARD && !isset($this->_paymentFields)) { - $legacyCreditCardExpiryCheck = TRUE; - } - if ($legacyCreditCardExpiryCheck || in_array('credit_card_exp_date', array_keys($this->_paymentFields))) { - $this->_params['year'] = CRM_Core_Payment_Form::getCreditCardExpirationYear($this->_params); - $this->_params['month'] = CRM_Core_Payment_Form::getCreditCardExpirationMonth($this->_params); - } - $this->_params['ip_address'] = CRM_Utils_System::ipAddress(); - $this->_params['amount'] = $this->_params['total_amount']; - $this->_params['amount_level'] = 0; - $this->_params['description'] = ts('Office Credit Card contribution'); - $this->_params['currencyID'] = CRM_Utils_Array::value('currency', - $this->_params, - CRM_Core_Config::singleton()->defaultCurrency + $pane = array( + 'url' => CRM_Utils_System::url('civicrm/contact/view/contribution', $urlParams), + 'open' => $open, + 'id' => $type, ); - $this->_params['payment_action'] = 'Sale'; - if (!empty($this->_params['receive_date'])) { - $this->_params['receive_date'] = CRM_Utils_Date::processDate($this->_params['receive_date'], $this->_params['receive_date_time']); - } - if (!empty($params['soft_credit_to'])) { - $this->_params['soft_credit_to'] = $params['soft_credit_to']; - $this->_params['pcp_made_through_id'] = $params['pcp_made_through_id']; + // See if we need to include this paneName in the current form. + if ($this->_formType == $type || !empty($_POST["hidden_{$type}"]) || + CRM_Utils_Array::value("hidden_{$type}", $defaults) + ) { + $this->assign('showAdditionalInfo', TRUE); + $pane['open'] = 'true'; } - $this->_params['pcp_display_in_roll'] = CRM_Utils_Array::value('pcp_display_in_roll', $params); - $this->_params['pcp_roll_nickname'] = CRM_Utils_Array::value('pcp_roll_nickname', $params); - $this->_params['pcp_personal_note'] = CRM_Utils_Array::value('pcp_personal_note', $params); - - //Add common data to formatted params - CRM_Contribute_Form_AdditionalInfo::postProcessCommon($params, $this->_params, $this); - - if (empty($this->_params['invoice_id'])) { - $this->_params['invoiceID'] = md5(uniqid(rand(), TRUE)); + if ($type == 'CreditCard' || $type == 'DirectDebit') { + // @todo would be good to align tpl name with form name... + // @todo document why this hidden variable is required. + $this->add('hidden', 'hidden_' . $type, 1); + return $pane; } else { - $this->_params['invoiceID'] = $this->_params['invoice_id']; + $additionalInfoFormFunction = 'build' . $type; + CRM_Contribute_Form_AdditionalInfo::$additionalInfoFormFunction($this); + return $pane; } + } - // At this point we've created a contact and stored its address etc - // all the payment processors expect the name and address to be in the - // so we copy stuff over to first_name etc. - $paymentParams = $this->_params; - $paymentParams['contactID'] = $this->_contactID; - CRM_Core_Payment_Form::mapParams($this->_bltID, $this->_params, $paymentParams, TRUE); - - $contributionType = new CRM_Financial_DAO_FinancialType(); - $contributionType->id = $params['financial_type_id']; + /** + * Wrapper for unit testing the post process submit function. + * + * (If we expose through api we can get default additions 'for free'). + * + * @param array $params + */ + public function testSubmit($params) { + $defaults = array( + 'soft_credit_contact_id' => array(), + 'receipt_date' => '', + 'receipt_date_time' => '', + 'cancel_date' => '', + 'cancel_date_time' => '', + ); - // Add some financial type details to the params list - // if folks need to use it. - $paymentParams['contributionType_name'] = $this->_params['contributionType_name'] = $contributionType->name; - $paymentParams['contributionPageID'] = NULL; + $this->submit(array_merge($defaults, $params)); + } - if (!empty($this->_params['is_email_receipt'])) { - $paymentParams['email'] = $this->userEmail; - $paymentParams['is_email_receipt'] = 1; - } - else { - $paymentParams['is_email_receipt'] = 0; - $this->_params['is_email_receipt'] = 0; + /** + * @param array $submittedValues + * + * @return array + * @throws \Exception + */ + protected function submit($submittedValues) { + $softParams = $softIDs = array(); + $pId = $contribution = $isRelatedId = FALSE; + if (!empty($submittedValues['price_set_id']) && $this->_action & CRM_Core_Action::UPDATE) { + $line = CRM_Price_BAO_LineItem::getLineItems($this->_id, 'contribution'); + $lineID = key($line); + $priceSetId = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceField', CRM_Utils_Array::value('price_field_id', $line[$lineID]), 'price_set_id'); + $quickConfig = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceSet', $priceSetId, 'is_quick_config'); + if ($quickConfig) { + CRM_Price_BAO_LineItem::deleteLineItems($this->_id, 'civicrm_contribution'); + } } - if (!empty($this->_params['receive_date'])) { - $paymentParams['receive_date'] = $this->_params['receive_date']; + + // Process price set and get total amount and line items. + $lineItem = array(); + $priceSetId = CRM_Utils_Array::value('price_set_id', $submittedValues); + if (empty($priceSetId) && !$this->_id) { + $this->_priceSetId = $priceSetId = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceSet', 'default_contribution_amount', 'id', 'name'); + $this->_priceSet = current(CRM_Price_BAO_PriceSet::getSetDetail($priceSetId)); + $fieldID = key($this->_priceSet['fields']); + $fieldValueId = key($this->_priceSet['fields'][$fieldID]['options']); + $this->_priceSet['fields'][$fieldID]['options'][$fieldValueId]['amount'] = $submittedValues['total_amount']; + $submittedValues['price_' . $fieldID] = 1; } - // For recurring contribution, create Contribution Record first. - // Contribution ID, Recurring ID and Contact ID needed - // When we get a callback from the payment processor, CRM-7115 + if ($priceSetId) { + CRM_Price_BAO_PriceSet::processAmount($this->_priceSet['fields'], + $submittedValues, $lineItem[$priceSetId]); - if (!empty($paymentParams['is_recur'])) { - $contribution = CRM_Contribute_Form_Contribution_Confirm::processContribution($this, - $this->_params, - NULL, - $this->_contactID, - $contributionType, - TRUE, - FALSE, - $isTest, - $this->_lineItem - ); - $paymentParams['contributionID'] = $contribution->id; - $paymentParams['contributionTypeID'] = $contribution->financial_type_id; - $paymentParams['contributionPageID'] = $contribution->contribution_page_id; - $paymentParams['contributionRecurID'] = $contribution->contribution_recur_id; + // Unset tax amount for offline 'is_quick_config' contribution. + if ($this->_priceSet['is_quick_config'] && + !array_key_exists($submittedValues['financial_type_id'], CRM_Core_PseudoConstant::getTaxRates()) + ) { + unset($submittedValues['tax_amount']); + } + $submittedValues['total_amount'] = CRM_Utils_Array::value('amount', $submittedValues); } - $result = array(); - if ($paymentParams['amount'] > 0.0) { - // force a re-get of the payment processor in case the form changed it, CRM-7179 - $payment = CRM_Core_Payment::singleton($this->_mode, $this->_paymentProcessor, $this, TRUE); - try { - $result = $payment->doPayment($paymentParams, 'contribute'); + if ($this->_id) { + if ($this->_compId) { + if ($this->_context == 'participant') { + $pId = $this->_compId; + } + elseif ($this->_context == 'membership') { + $isRelatedId = TRUE; + } + else { + $pId = CRM_Core_DAO::getFieldValue('CRM_Event_DAO_ParticipantPayment', $this->_id, 'participant_id', 'contribution_id'); + } } - catch (CRM_Core_Exception $e) { - $message = ts("Payment Processor Error message") . $e->getMessage(); - $this->cleanupDBAfterPaymentFailure($paymentParams, $message); - // Set the contribution mode. - $urlParams = "action=add&cid={$this->_contactID}"; - if ($this->_mode) { - $urlParams .= "&mode={$this->_mode}"; + else { + $contributionDetails = CRM_Contribute_BAO_Contribution::getComponentDetails($this->_id); + if (array_key_exists('membership', $contributionDetails)) { + $isRelatedId = TRUE; } - if (!empty($this->_ppID)) { - $urlParams .= "&context=pledge&ppid={$this->_ppID}"; + elseif (array_key_exists('participant', $contributionDetails)) { + $pId = $contributionDetails['participant']; } - CRM_Core_Error::statusBounce($message, $urlParams, ts('Payment Processor Error')); } } + if (!$priceSetId && !empty($submittedValues['total_amount']) && $this->_id) { + // CRM-10117 update the line items for participants. + if ($pId) { + $entityTable = 'participant'; + $entityID = $pId; + $isRelatedId = FALSE; + $participantParams = array( + 'fee_amount' => $submittedValues['total_amount'], + 'id' => $entityID, + ); + CRM_Event_BAO_Participant::add($participantParams); + if (empty($this->_lineItems)) { + $this->_lineItems[] = CRM_Price_BAO_LineItem::getLineItems($entityID, 'participant', 1); + } + } + else { + $entityTable = 'contribution'; + $entityID = $this->_id; + } - $this->_params = array_merge($this->_params, $result); - - $this->_params['receive_date'] = $now; - - if (!empty($this->_params['is_email_receipt'])) { - $this->_params['receipt_date'] = $now; - } - else { - $this->_params['receipt_date'] = CRM_Utils_Date::processDate($this->_params['receipt_date'], - $params['receipt_date_time'], TRUE - ); - } - - $this->set('params', $this->_params); - $this->assign('trxn_id', $result['trxn_id']); - $this->assign('receive_date', $this->_params['receive_date']); + $lineItems = CRM_Price_BAO_LineItem::getLineItems($entityID, $entityTable, NULL, TRUE, $isRelatedId); + foreach (array_keys($lineItems) as $id) { + $lineItems[$id]['id'] = $id; + } + $itemId = key($lineItems); + if ($itemId && !empty($lineItems[$itemId]['price_field_id'])) { + $this->_priceSetId = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceField', $lineItems[$itemId]['price_field_id'], 'price_set_id'); + } - // Result has all the stuff we need - // lets archive it to a financial transaction - if ($contributionType->is_deductible) { - $this->assign('is_deductible', TRUE); - $this->set('is_deductible', TRUE); - } + if ($this->_priceSetId && CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceSet', $this->_priceSetId, 'is_quick_config')) { + $lineItems[$itemId]['unit_price'] = $lineItems[$itemId]['line_total'] = CRM_Utils_Rule::cleanMoney(CRM_Utils_Array::value('total_amount', $submittedValues)); - // Set source if not set - if (empty($this->_params['source'])) { - $userID = CRM_Core_Session::singleton()->get('userID'); - $userSortName = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $userID, - 'sort_name' - ); - $this->_params['source'] = ts('Submit Credit Card Payment by: %1', array(1 => $userSortName)); + // Update line total and total amount with tax on edit. + $financialItemsId = CRM_Core_PseudoConstant::getTaxRates(); + if (array_key_exists($submittedValues['financial_type_id'], $financialItemsId)) { + $lineItems[$itemId]['tax_rate'] = $financialItemsId[$submittedValues['financial_type_id']]; + } + else { + $lineItems[$itemId]['tax_rate'] = $lineItems[$itemId]['tax_amount'] = ""; + $submittedValues['tax_amount'] = 'null'; + } + if ($lineItems[$itemId]['tax_rate']) { + $lineItems[$itemId]['tax_amount'] = ($lineItems[$itemId]['tax_rate'] / 100) * $lineItems[$itemId]['line_total']; + $submittedValues['total_amount'] = $lineItems[$itemId]['line_total'] + $lineItems[$itemId]['tax_amount']; + $submittedValues['tax_amount'] = $lineItems[$itemId]['tax_amount']; + } + } + // CRM-10117 update the line items for participants. + if (!empty($lineItems[$itemId]['price_field_id'])) { + $lineItem[$this->_priceSetId] = $lineItems; + } } - // Build custom data getFields array - $customFieldsContributionType = CRM_Core_BAO_CustomField::getFields('Contribution', FALSE, FALSE, - CRM_Utils_Array::value('financial_type_id', $params) - ); - $customFields = CRM_Utils_Array::crmArrayMerge($customFieldsContributionType, - CRM_Core_BAO_CustomField::getFields('Contribution', FALSE, FALSE, NULL, NULL, TRUE) - ); - $params['custom'] = CRM_Core_BAO_CustomField::postProcess($params, - $customFields, - $this->_id, - 'Contribution' - ); - if (empty($paymentParams['is_recur'])) { - $contribution = CRM_Contribute_Form_Contribution_Confirm::processContribution($this, - $this->_params, - $result, - $this->_contactID, - $contributionType, - FALSE, FALSE, - $isTest, - $this->_lineItem - ); + $isQuickConfig = 0; + if ($this->_priceSetId && CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceSet', $this->_priceSetId, 'is_quick_config')) { + $isQuickConfig = 1; } - - // Send receipt mail. - if ($contribution->id && !empty($this->_params['is_email_receipt'])) { - $this->_params['trxn_id'] = CRM_Utils_Array::value('trxn_id', $result); - $this->_params['contact_id'] = $this->_contactID; - $this->_params['contribution_id'] = $contribution->id; - $sendReceipt = CRM_Contribute_Form_AdditionalInfo::emailReceipt($this, $this->_params, TRUE); + //CRM-11529 for quick config back office transactions + //when financial_type_id is passed in form, update the + //line items with the financial type selected in form + if ($isQuickConfig && !empty($submittedValues['financial_type_id']) && CRM_Utils_Array::value($this->_priceSetId, $lineItem) + ) { + foreach ($lineItem[$this->_priceSetId] as &$values) { + $values['financial_type_id'] = $submittedValues['financial_type_id']; + } } - //process the note - if ($contribution->id && isset($params['note'])) { - CRM_Contribute_Form_AdditionalInfo::processNote($params, $contactID, $contribution->id, NULL); - } - //process premium - if ($contribution->id && isset($params['product_name'][0])) { - CRM_Contribute_Form_AdditionalInfo::processPremium($params, $contribution->id, NULL, $this->_options); + if (!isset($submittedValues['total_amount'])) { + $submittedValues['total_amount'] = CRM_Utils_Array::value('total_amount', $this->_values); } + $this->assign('lineItem', !empty($lineItem) && !$isQuickConfig ? $lineItem : FALSE); - //update pledge payment status. - if ($this->_ppID && $contribution->id) { - // Store contribution id in payment record. - CRM_Core_DAO::setFieldValue('CRM_Pledge_DAO_PledgePayment', $this->_ppID, 'contribution_id', $contribution->id); - - CRM_Pledge_BAO_PledgePayment::updatePledgePaymentStatus($this->_pledgeID, - array($this->_ppID), - $contribution->contribution_status_id, - NULL, - $contribution->total_amount + if (!empty($submittedValues['pcp_made_through_id'])) { + $pcp = array(); + $fields = array( + 'pcp_made_through_id', + 'pcp_display_in_roll', + 'pcp_roll_nickname', + 'pcp_personal_note', ); + foreach ($fields as $f) { + $pcp[$f] = CRM_Utils_Array::value($f, $submittedValues); + } } - if ($contribution->id) { - $statusMsg = ts('The contribution record has been processed.'); - if (!empty($this->_params['is_email_receipt']) && $sendReceipt) { - $statusMsg .= ' ' . ts('A receipt has been emailed to the contributor.'); + $isEmpty = array_keys(array_flip($submittedValues['soft_credit_contact_id'])); + if ($this->_id && count($isEmpty) == 1 && key($isEmpty) == NULL) { + //Delete existing soft credit records if soft credit list is empty on update + CRM_Contribute_BAO_ContributionSoft::del(array('contribution_id' => $this->_id)); + } + else { + //build soft credit params + foreach ($submittedValues['soft_credit_contact_id'] as $key => $val) { + if ($val && $submittedValues['soft_credit_amount'][$key]) { + $softParams[$key]['contact_id'] = $val; + $softParams[$key]['amount'] = CRM_Utils_Rule::cleanMoney($submittedValues['soft_credit_amount'][$key]); + $softParams[$key]['soft_credit_type_id'] = $submittedValues['soft_credit_type'][$key]; + if (!empty($submittedValues['soft_credit_id'][$key])) { + $softIDs[] = $softParams[$key]['id'] = $submittedValues['soft_credit_id'][$key]; + } + } } - CRM_Core_Session::setStatus($statusMsg, ts('Complete'), 'success'); } - } - /** - * Clean up DB after payment fails. - * - * This function removes related DB entries. Note that it has been agreed in principle, - * but not implemented, that contributions should be retained as 'Failed' rather than - * deleted. - * - * @todo it doesn't clean up line items. - * - * @param array $paymentParams - * @param string $message - */ - public function cleanupDBAfterPaymentFailure($paymentParams, $message) { - // Make sure to cleanup db for recurring case. - if (!empty($paymentParams['contributionID'])) { - CRM_Core_Error::debug_log_message($message . - "contact id={$this->_contactID} (deleting contribution {$paymentParams['contributionID']}"); - CRM_Contribute_BAO_Contribution::deleteContribution($paymentParams['contributionID']); - } - if (!empty($paymentParams['contributionRecurID'])) { - CRM_Core_Error::debug_log_message($message . - "contact id={$this->_contactID} (deleting recurring contribution {$paymentParams['contributionRecurID']}"); - CRM_Contribute_BAO_ContributionRecur::deleteRecurContribution($paymentParams['contributionRecurID']); + // set the contact, when contact is selected + if (!empty($submittedValues['contact_id'])) { + $this->_contactID = $submittedValues['contact_id']; } - } - /** - * Generate the data to construct a snippet based pane. - * - * This form also assigns the showAdditionalInfo var based on historical code. - * This appears to mean 'there is a pane to show'. - * - * @param string $type - * Type of Pane - this is generally used to determine the function name used to build it - * - e.g CreditCard, AdditionalDetail - * @param array $defaults - * - * @return array - * We aim to further refactor & simplify this but currently - * - the panes array - * - should additional info be shown? - */ - protected function generatePane($type, $defaults) { - $urlParams = "snippet=4&formType={$type}"; + // Credit Card Contribution. if ($this->_mode) { - $urlParams .= "&mode={$this->_mode}"; + $this->processCreditCard($submittedValues, $lineItem); + return FALSE; } + else { + // Offline Contribution. + $submittedValues = $this->unsetCreditCardFields($submittedValues); - $open = 'false'; - if ($type == 'CreditCard' || - $type == 'DirectDebit' - ) { - $open = 'true'; - } + // get the required field value only. + $formValues = $submittedValues; + $params = $ids = array(); - $pane = array( - 'url' => CRM_Utils_System::url('civicrm/contact/view/contribution', $urlParams), - 'open' => $open, - 'id' => $type, - ); + $params['contact_id'] = $this->_contactID; - // See if we need to include this paneName in the current form. - if ($this->_formType == $type || !empty($_POST["hidden_{$type}"]) || - CRM_Utils_Array::value("hidden_{$type}", $defaults) - ) { - $this->assign('showAdditionalInfo', TRUE); - $pane['open'] = 'true'; - } + $params['currency'] = $this->getCurrency($submittedValues); - if ($type == 'CreditCard' || $type == 'DirectDebit') { - // @todo would be good to align tpl name with form name... - // @todo document why this hidden variable is required. - $this->add('hidden', 'hidden_' . $type, 1); - return $pane; - } - else { - $additionalInfoFormFunction = 'build' . $type; - CRM_Contribute_Form_AdditionalInfo::$additionalInfoFormFunction($this); - return $pane; - } - } + $fields = array( + 'financial_type_id', + 'contribution_status_id', + 'payment_instrument_id', + 'cancel_reason', + 'source', + 'check_number', + ); + foreach ($fields as $f) { + $params[$f] = CRM_Utils_Array::value($f, $formValues); + } - /** - * Wrapper for unit testing the post process submit function. - * - * (If we expose through api we can get default additions 'for free'). - * - * @param array $params - */ - public function testSubmit($params) { - $defaults = array( - 'soft_credit_contact_id' => array(), - 'receipt_date' => '', - 'receipt_date_time' => '', - 'cancel_date' => '', - 'cancel_date_time' => '', - ); + if (!empty($pcp)) { + $params['pcp'] = $pcp; + } + if (!empty($softParams)) { + $params['soft_credit'] = $softParams; + $params['soft_credit_ids'] = $softIDs; + } - $this->submit(array_merge($defaults, $params)); + // CRM-5740 if priceset is used, no need to cleanup money. + if ($priceSetId) { + $params['skipCleanMoney'] = 1; + } + + $dates = array( + 'receive_date', + 'receipt_date', + 'cancel_date', + ); + + foreach ($dates as $d) { + $params[$d] = CRM_Utils_Date::processDate($formValues[$d], $formValues[$d . '_time'], TRUE); + } + + if (!empty($formValues['is_email_receipt'])) { + $params['receipt_date'] = date("Y-m-d"); + } + + if ($params['contribution_status_id'] == CRM_Core_OptionGroup::getValue('contribution_status', 'Cancelled', 'name') + || $params['contribution_status_id'] == CRM_Core_OptionGroup::getValue('contribution_status', 'Refunded', 'name') + ) { + if (CRM_Utils_System::isNull(CRM_Utils_Array::value('cancel_date', $params))) { + $params['cancel_date'] = date('Y-m-d'); + } + } + else { + $params['cancel_date'] = $params['cancel_reason'] = 'null'; + } + + // Set is_pay_later flag for back-office offline Pending status contributions CRM-8996 + // else if contribution_status is changed to Completed is_pay_later flag is changed to 0, CRM-15041 + if ($params['contribution_status_id'] == CRM_Core_OptionGroup::getValue('contribution_status', 'Pending', 'name')) { + $params['is_pay_later'] = 1; + } + elseif ($params['contribution_status_id'] == CRM_Core_OptionGroup::getValue('contribution_status', 'Completed', 'name')) { + $params['is_pay_later'] = 0; + } + + $ids['contribution'] = $params['id'] = $this->_id; + + // Add Additional common information to formatted params. + CRM_Contribute_Form_AdditionalInfo::postProcessCommon($formValues, $params, $this); + if ($pId) { + $params['contribution_mode'] = 'participant'; + $params['participant_id'] = $pId; + $params['skipLineItem'] = 1; + } + elseif ($isRelatedId) { + $params['contribution_mode'] = 'membership'; + } + $params['line_item'] = $lineItem; + $params['payment_processor_id'] = $params['payment_processor'] = CRM_Utils_Array::value('id', $this->_paymentProcessor); + if (isset($submittedValues['tax_amount'])) { + $params['tax_amount'] = $submittedValues['tax_amount']; + } + //create contribution. + if ($isQuickConfig) { + $params['is_quick_config'] = 1; + } + + // CRM-11956 + // if non_deductible_amount exists i.e. Additional Details field set was opened [and staff typed something] - + // if non_deductible_amount does NOT exist - then calculate it depending on: + // $ContributionType->is_deductible and whether there is a product (premium). + if (empty($params['non_deductible_amount'])) { + $contributionType = new CRM_Financial_DAO_FinancialType(); + $contributionType->id = $params['financial_type_id']; + if (!$contributionType->find(TRUE)) { + CRM_Core_Error::fatal('Could not find a system table'); + } + if ($contributionType->is_deductible) { + + if (isset($formValues['product_name'][0])) { + $selectProduct = $formValues['product_name'][0]; + } + // if there is a product - compare the value to the contribution amount + if (isset($selectProduct)) { + $productDAO = new CRM_Contribute_DAO_Product(); + $productDAO->id = $selectProduct; + $productDAO->find(TRUE); + // product value exceeds contribution amount + if ($params['total_amount'] < $productDAO->price) { + $params['non_deductible_amount'] = $params['total_amount']; + } + // product value does NOT exceed contribution amount + else { + $params['non_deductible_amount'] = $productDAO->price; + } + } + // contribution is deductible - but there is no product + else { + $params['non_deductible_amount'] = '0.00'; + } + } + // contribution is NOT deductible + else { + $params['non_deductible_amount'] = $params['total_amount']; + } + } + + $contribution = CRM_Contribute_BAO_Contribution::create($params, $ids); + + // process associated membership / participant, CRM-4395 + $relatedComponentStatusMsg = NULL; + if ($contribution->id && $this->_action & CRM_Core_Action::UPDATE) { + $relatedComponentStatusMsg = $this->updateRelatedComponent($contribution->id, + $contribution->contribution_status_id, + CRM_Utils_Array::value('contribution_status_id', + $this->_values + ), + $contribution->receive_date + ); + } + + //process note + if ($contribution->id && isset($formValues['note'])) { + CRM_Contribute_Form_AdditionalInfo::processNote($formValues, $this->_contactID, $contribution->id, $this->_noteID); + } + + //process premium + if ($contribution->id && isset($formValues['product_name'][0])) { + CRM_Contribute_Form_AdditionalInfo::processPremium($formValues, $contribution->id, + $this->_premiumID, $this->_options + ); + } + + // assign tax calculation for contribution receipts + $taxRate = array(); + $getTaxDetails = FALSE; + $invoiceSettings = CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::CONTRIBUTE_PREFERENCES_NAME, 'contribution_invoice_settings'); + $invoicing = CRM_Utils_Array::value('invoicing', $invoiceSettings); + if ($invoicing) { + if ($this->_action & CRM_Core_Action::ADD) { + $line = $lineItem; + } + elseif ($this->_action & CRM_Core_Action::UPDATE) { + $line = $this->_lineItems; + } + foreach ($line as $key => $value) { + foreach ($value as $v) { + if (isset($taxRate[(string) CRM_Utils_Array::value('tax_rate', $v)])) { + $taxRate[(string) $v['tax_rate']] = $taxRate[(string) $v['tax_rate']] + CRM_Utils_Array::value('tax_amount', $v); + } + else { + if (isset($v['tax_rate'])) { + $taxRate[(string) $v['tax_rate']] = CRM_Utils_Array::value('tax_amount', $v); + $getTaxDetails = TRUE; + } + } + } + } + } + + if ($invoicing) { + if ($this->_action & CRM_Core_Action::UPDATE) { + if (isset($submittedValues['tax_amount'])) { + $totalTaxAmount = $submittedValues['tax_amount']; + } + else { + $totalTaxAmount = $this->_values['tax_amount']; + } + $this->assign('totalTaxAmount', $totalTaxAmount); + $this->assign('dataArray', $taxRate); + } + else { + if (!empty($submittedValues['price_set_id'])) { + $this->assign('totalTaxAmount', $submittedValues['tax_amount']); + $this->assign('getTaxDetails', $getTaxDetails); + $this->assign('dataArray', $taxRate); + $this->assign('taxTerm', CRM_Utils_Array::value('tax_term', $invoiceSettings)); + } + else { + $this->assign('totalTaxAmount', CRM_Utils_Array::value('tax_amount', $submittedValues)); + } + } + } + + //send receipt mail. + if ($contribution->id && !empty($formValues['is_email_receipt'])) { + $formValues['contact_id'] = $this->_contactID; + $formValues['contribution_id'] = $contribution->id; + + $formValues += CRM_Contribute_BAO_ContributionSoft::getSoftContribution($contribution->id); + + // to get 'from email id' for send receipt + $this->fromEmailId = $formValues['from_email_address']; + $sendReceipt = CRM_Contribute_Form_AdditionalInfo::emailReceipt($this, $formValues); + } + + $pledgePaymentId = CRM_Core_DAO::getFieldValue('CRM_Pledge_DAO_PledgePayment', + $contribution->id, + 'id', + 'contribution_id' + ); + + //update pledge payment status. + if ((($this->_ppID && $contribution->id) && $this->_action & CRM_Core_Action::ADD) || + (($pledgePaymentId) && $this->_action & CRM_Core_Action::UPDATE) + ) { + + if ($this->_ppID) { + //store contribution id in payment record. + CRM_Core_DAO::setFieldValue('CRM_Pledge_DAO_PledgePayment', $this->_ppID, 'contribution_id', $contribution->id); + } + else { + $this->_ppID = CRM_Core_DAO::getFieldValue('CRM_Pledge_DAO_PledgePayment', + $contribution->id, + 'id', + 'contribution_id' + ); + $this->_pledgeID = CRM_Core_DAO::getFieldValue('CRM_Pledge_DAO_PledgePayment', + $contribution->id, + 'pledge_id', + 'contribution_id' + ); + } + + $adjustTotalAmount = FALSE; + if (CRM_Utils_Array::value('option_type', $formValues) == 2) { + $adjustTotalAmount = TRUE; + } + + $updatePledgePaymentStatus = FALSE; + //do only if either the status or the amount has changed + if ($this->_action & CRM_Core_Action::ADD) { + $updatePledgePaymentStatus = TRUE; + } + elseif ($this->_action & CRM_Core_Action::UPDATE && (($this->_defaults['contribution_status_id'] != $formValues['contribution_status_id']) || + ($this->_defaults['total_amount'] != $formValues['total_amount'])) + ) { + $updatePledgePaymentStatus = TRUE; + } + + if ($updatePledgePaymentStatus) { + CRM_Pledge_BAO_PledgePayment::updatePledgePaymentStatus($this->_pledgeID, + array($this->_ppID), + $contribution->contribution_status_id, + NULL, + $contribution->total_amount, + $adjustTotalAmount + ); + } + } + + $statusMsg = ts('The contribution record has been saved.'); + if (!empty($formValues['is_email_receipt']) && $sendReceipt) { + $statusMsg .= ' ' . ts('A receipt has been emailed to the contributor.'); + } + + if ($relatedComponentStatusMsg) { + $statusMsg .= ' ' . $relatedComponentStatusMsg; + } + + CRM_Core_Session::setStatus($statusMsg, ts('Saved'), 'success'); + return $contribution; + //Offline Contribution ends. + } } }