/** * @param $input * @param $ids * @param $objects * @param $first * * @return bool */ public function recur(&$input, &$ids, &$objects, $first) { if (!isset($input['txnType'])) { CRM_Core_Error::debug_log_message("Could not find txn_type in input request"); echo "Failure: Invalid parameters<p>"; return FALSE; } if ($input['txnType'] == 'subscr_payment' && $input['paymentStatus'] != 'Completed') { CRM_Core_Error::debug_log_message("Ignore all IPN payments that are not completed"); echo "Failure: Invalid parameters<p>"; return FALSE; } $recur =& $objects['contributionRecur']; // make sure the invoice ids match // make sure the invoice is valid and matches what we have in the contribution record if ($recur->invoice_id != $input['invoice']) { CRM_Core_Error::debug_log_message("Invoice values dont match between database and IPN request"); echo "Failure: Invoice values dont match between database and IPN request<p>"; return FALSE; } $now = date('YmdHis'); // fix dates that already exist $dates = array('create', 'start', 'end', 'cancel', 'modified'); foreach ($dates as $date) { $name = "{$date}_date"; if ($recur->{$name}) { $recur->{$name} = CRM_Utils_Date::isoToMysql($recur->{$name}); } } $sendNotification = FALSE; $subscriptionPaymentStatus = NULL; //set transaction type $txnType = $_POST['txn_type']; switch ($txnType) { case 'subscr_signup': $recur->create_date = $now; //some times subscr_signup response come after the //subscr_payment and set to pending mode. $statusID = CRM_Core_DAO::getFieldValue('CRM_Contribute_DAO_ContributionRecur', $recur->id, 'contribution_status_id'); if ($statusID != 5) { $recur->contribution_status_id = 2; } $recur->processor_id = $_POST['subscr_id']; $recur->trxn_id = $recur->processor_id; $sendNotification = TRUE; $subscriptionPaymentStatus = CRM_Core_Payment::RECURRING_PAYMENT_START; break; case 'subscr_eot': if ($recur->contribution_status_id != 3) { $recur->contribution_status_id = 1; } $recur->end_date = $now; $sendNotification = TRUE; $subscriptionPaymentStatus = CRM_Core_Payment::RECURRING_PAYMENT_END; break; case 'subscr_cancel': $recur->contribution_status_id = 3; $recur->cancel_date = $now; break; case 'subscr_failed': $recur->contribution_status_id = 4; $recur->modified_date = $now; break; case 'subscr_modify': CRM_Core_Error::debug_log_message("We do not handle modifications to subscriptions right now"); echo "Failure: We do not handle modifications to subscriptions right now<p>"; return FALSE; case 'subscr_payment': if ($first) { $recur->start_date = $now; } else { $recur->modified_date = $now; } // make sure the contribution status is not done // since order of ipn's is unknown if ($recur->contribution_status_id != 1) { $recur->contribution_status_id = 5; } break; } $recur->save(); if ($sendNotification) { $autoRenewMembership = FALSE; if ($recur->id && isset($ids['membership']) && $ids['membership']) { $autoRenewMembership = TRUE; } //send recurring Notification email for user CRM_Contribute_BAO_ContributionPage::recurringNotify($subscriptionPaymentStatus, $ids['contact'], $ids['contributionPage'], $recur, $autoRenewMembership); } if ($txnType != 'subscr_payment') { return; } if (!$first) { //check if this contribution transaction is already processed //if not create a contribution and then get it processed $contribution = new CRM_Contribute_BAO_Contribution(); $contribution->trxn_id = $input['trxn_id']; if ($contribution->trxn_id && $contribution->find()) { CRM_Core_Error::debug_log_message("returning since contribution has already been handled"); echo "Success: Contribution has already been handled<p>"; return TRUE; } $contribution->contact_id = $ids['contact']; $contribution->financial_type_id = $objects['contributionType']->id; $contribution->contribution_page_id = $ids['contributionPage']; $contribution->contribution_recur_id = $ids['contributionRecur']; $contribution->receive_date = $now; $contribution->currency = $objects['contribution']->currency; $contribution->payment_instrument_id = $objects['contribution']->payment_instrument_id; $contribution->amount_level = $objects['contribution']->amount_level; $contribution->campaign_id = $objects['contribution']->campaign_id; $objects['contribution'] =& $contribution; } $this->single($input, $ids, $objects, TRUE, $first); }
/** * Given the list of params in the params array, fetch the object * and store the values in the values array * * @param array $params * Input parameters to find object. * @param array $values * Output values of the object. * @param array $ids * The array that holds all the db ids. * * @return CRM_Contribute_BAO_Contribution|null * The found object or null */ public static function getValues($params, &$values, &$ids) { if (empty($params)) { return NULL; } $contribution = new CRM_Contribute_BAO_Contribution(); $contribution->copyValues($params); if ($contribution->find(TRUE)) { $ids['contribution'] = $contribution->id; CRM_Core_DAO::storeValues($contribution, $values); return $contribution; } $null = NULL; // return by reference return $null; }
/** * Process failed transaction - would be nice to do this through api too but for now lets put in * here - this is a copy & paste of the completetransaction api * @param unknown $contributionID */ function processFailedTransaction($contributionID) { $input = $ids = array(); $contribution = new CRM_Contribute_BAO_Contribution(); $contribution->id = $contributionID; $contribution->find(TRUE); if (!$contribution->id == $contributionID) { throw new Exception('A valid contribution ID is required', 'invalid_data'); } try { if (!$contribution->loadRelatedObjects($input, $ids, FALSE, TRUE)) { throw new Exception('failed to load related objects'); } $objects = $contribution->_relatedObjects; $objects['contribution'] =& $contribution; $input['component'] = $contribution->_component; $input['is_test'] = $contribution->is_test; $input['amount'] = $contribution->total_amount; // @todo required for base ipn but problematic as api layer handles this $transaction = new CRM_Core_Transaction(); $ipn = new CRM_Core_Payment_BaseIPN(); $ipn->failed($objects, $transaction, $input); } catch (Exception $e) { } }
/** * Complete an existing (pending) transaction. * * This will update related entities (participant, membership, pledge etc) * and take any complete actions from the contribution page (e.g. send receipt). * * @todo - most of this should live in the BAO layer but as we want it to be an addition * to 4.3 which is already stable we should add it to the api layer & re-factor into the BAO layer later * * @param array $params * Input parameters. * * @throws API_Exception * Api result array. */ function civicrm_api3_contribution_repeattransaction(&$params) { $input = $ids = array(); civicrm_api3_verify_one_mandatory($params, NULL, array('contribution_recur_id', 'original_contribution_id')); if (empty($params['original_contribution_id'])) { $params['original_contribution_id'] = civicrm_api3('contribution', 'getvalue', array('return' => 'id', 'contribution_recur_id' => $params['contribution_recur_id'], 'options' => array('limit' => 1, 'sort' => 'id DESC'))); } $contribution = new CRM_Contribute_BAO_Contribution(); $contribution->id = $params['original_contribution_id']; if (!$contribution->find(TRUE)) { throw new API_Exception('A valid original contribution ID is required', 'invalid_data'); } $original_contribution = clone $contribution; try { if (!$contribution->loadRelatedObjects($input, $ids, TRUE)) { throw new API_Exception('failed to load related objects'); } unset($contribution->id, $contribution->receive_date, $contribution->invoice_id); $contribution->receive_date = $params['receive_date']; $passThroughParams = array('trxn_id', 'total_amount', 'campaign_id', 'fee_amount', 'financial_type_id', 'contribution_status_id'); $input = array_intersect_key($params, array_fill_keys($passThroughParams, NULL)); $params = _ipn_process_transaction($params, $contribution, $input, $ids, $original_contribution); } catch (Exception $e) { throw new API_Exception('failed to load related objects' . $e->getMessage() . "\n" . $e->getTraceAsString()); } }
/** * The function gets called when the state(CHARGED, CANCELLED..) changes for an order * * @param string $status status of the transaction send by google * @param array $privateData contains the name value pair of <merchant-private-data> * * @return void * */ function orderStateChange($status, $dataRoot, $privateData, $component) { $input = $objects = $ids = array(); $input['component'] = strtolower($component); $ids['contributionRecur'] = self::retrieve('contributionRecurID', 'Integer', $privateData, FALSE); $serial = $dataRoot['serial-number']; $orderNo = $dataRoot['google-order-number']['VALUE']; $contribution = new CRM_Contribute_BAO_Contribution(); $contribution->invoice_id = $orderNo; if (!$contribution->find(TRUE)) { CRM_Core_Error::debug_log_message("orderStateChange: Could not find contribution record with invoice id: {$serial}"); return; } // Google sends the charged notification twice. // So to make sure, code is not executed again. if ($contribution->contribution_status_id == 1) { CRM_Core_Error::debug_log_message("Contribution already handled (ContributionID = {$contribution->id})."); return; } // make sure invoice is set to serial no for recurring payments, to avoid violating uniqueness $contribution->invoice_id = $ids['contributionRecur'] ? $serial : $orderNo; $objects['contribution'] =& $contribution; $ids['contribution'] = $contribution->id; $ids['contact'] = $contribution->contact_id; $ids['event'] = $ids['participant'] = $ids['membership'] = NULL; $ids['contributionPage'] = NULL; if ($input['component'] == "event") { list($ids['event'], $ids['participant']) = explode(CRM_Core_DAO::VALUE_SEPARATOR, $contribution->trxn_id); } else { $ids['related_contact'] = NULL; $ids['onbehalf_dupe_alert'] = NULL; if ($contribution->trxn_id) { list($ids['membership'], $ids['related_contact'], $ids['onbehalf_dupe_alert']) = explode(CRM_Core_DAO::VALUE_SEPARATOR, $contribution->trxn_id); } foreach (array('membership', 'related_contact', 'onbehalf_dupe_alert') as $fld) { if (!is_numeric($ids[$fld])) { unset($ids[$fld]); } } } $paymentProcessorID = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_PaymentProcessorType', 'Google_Checkout', 'id', 'payment_processor_type'); $this->loadObjects($input, $ids, $objects, TRUE, $paymentProcessorID); $transaction = new CRM_Core_Transaction(); // CRM_Core_Error::debug_var( 'c', $contribution ); if ($status == 'PAYMENT_DECLINED' || $status == 'CANCELLED_BY_GOOGLE' || $status == 'CANCELLED') { return $this->failed($objects, $transaction); } $input['amount'] = $contribution->total_amount; $input['fee_amount'] = NULL; $input['net_amount'] = NULL; $input['trxn_id'] = $ids['contributionRecur'] ? $serial : $dataRoot['google-order-number']['VALUE']; $input['is_test'] = $contribution->is_test; $recur = NULL; if ($ids['contributionRecur']) { $recur = $objects['contributionRecur']; } $this->completeTransaction($input, $ids, $objects, $transaction, $recur); $this->completeRecur($input, $ids, $objects); }
/** * Load objects related to contribution. * * @input array information from Payment processor * @param array $ids * @param array $objects * @param boolean $required * @param integer $paymentProcessorID * @param array $error_handling * @return multitype:number NULL |boolean */ function loadObjects(&$input, &$ids, &$objects, $required, $paymentProcessorID, $error_handling = NULL) { if (empty($error_handling)) { // default options are that we log an error & echo it out // note that we should refactor this error handling into error code @ some point // but for now setting up enough separation so we can do unit tests $error_handling = array('log_error' => 1, 'echo_error' => 1); } $ids['paymentProcessor'] = $paymentProcessorID; if (is_a($objects['contribution'], 'CRM_Contribute_BAO_Contribution')) { $contribution =& $objects['contribution']; } else { //legacy support - functions are 'used' to be able to pass in a DAO $contribution = new CRM_Contribute_BAO_Contribution(); $contribution->id = CRM_Utils_Array::value('contribution', $ids); $contribution->find(TRUE); $objects['contribution'] =& $contribution; } try { $success = $contribution->loadRelatedObjects($input, $ids, $required); } catch (Exception $e) { $success = FALSE; if (CRM_Utils_Array::value('log_error', $error_handling)) { CRM_Core_Error::debug_log_message($e->getMessage()); } if (CRM_Utils_Array::value('echo_error', $error_handling)) { echo $e->getMessage(); } if (CRM_Utils_Array::value('return_error', $error_handling)) { return array('is_error' => 1, 'error_message' => $e->getMessage()); } } $objects = array_merge($objects, $contribution->_relatedObjects); return $success; }
public function handlePaymentNotification() { $errors = array("101" => "Tarjeta caducada", "102" => "Tarjeta en excepción transitoria o bajo sospecha de fraude", "106" => "Intentos de PIN excedidos", "125" => "Tarjeta no efectiva", "129" => "Código de seguridad (CVV2/CVC2) incorrecto", "180" => "Tarjeta ajena al servicio", "184" => "Error en la autenticación del titular", "190" => "Denegación del emisor sin especificar motivo", "191" => "Fecha de caducidad errónea", "202" => "Tarjeta en excepción transitoria o bajo sospecha de fraude con retirada de tarjeta", "904" => "Comercio no registrado en FUC", "909" => "Error de sistema", "913" => "Pedido repetido", "944" => "Sesión Incorrecta", "950" => "Operación de devolución no permitida", "912" => "Emisor no disponible", "9912" => "Emisor no disponible", "9064" => "Número de posiciones de la tarjeta incorrecto", "9078" => "Tipo de operación no permitida para esa tarjeta", "9093" => "Tarjeta no existente", "9094" => "Rechazo servidores internacionales", "9104" => "Comercio con “titular seguro” y titular sin clave de compra segura", "9218" => "El comercio no permite op. seguras por entrada /operaciones", "9253" => "Tarjeta no cumple el check-digit", "9256" => "El comercio no puede realizar preautorizaciones", "9257" => "Esta tarjeta no permite operativa de preautorizaciones", "9261" => "Operación detenida por superar el control de restricciones en la entrada al SIS", "9915" => "A petición del usuario se ha cancelado el pago", "9929" => "Anulación de autorización en diferido realizada por el comercio", "9997" => "Se está procesando otra transacción en SIS con la misma tarjeta", "9998" => "Operación en proceso de solicitud de datos de tarjeta", "9999" => "Operación que ha sido redirigida al emisor a autenticar"); $module = self::retrieve('md', 'String', 'GET', false); $qfKey = self::retrieve('qfKey', 'String', 'GET', false); $miObj = new RedsysAPI(); $response = array(); $response["version"] = $_POST["Ds_SignatureVersion"]; $response["parameters"] = $_POST["Ds_MerchantParameters"]; $response["signature"] = $_POST["Ds_Signature"]; $decodecResponseJson = $miObj->decodeMerchantParameters($response["parameters"]); $decodecResponse = json_decode($decodecResponseJson); $firma = $miObj->createMerchantSignatureNotif($this->_paymentProcessor["password"], $response["parameters"]); // Validations if ($decodecResponse->Ds_MerchantCode != $this->_paymentProcessor["user_name"]) { CRM_Core_Error::debug_log_message("Redsys Response param Ds_MerchantCode incorrect"); return false; } // Contribution exists and is valid $contribution = new CRM_Contribute_BAO_Contribution(); $contribution->id = self::trimAmount($decodecResponse->Ds_Order); if (!$contribution->find(TRUE)) { CRM_Core_Error::debug_log_message("Could not find contribution record: {$contribution->id} in IPN request: " . print_r($params, TRUE)); echo "Failure: Could not find contribution record for {$contribution->id}<p>"; return FALSE; } if ($firma === $response["signature"]) { switch ($module) { case 'contribute': if ($decodecResponse->Ds_Response == self::REDSYS_RESPONSE_CODE_ACCEPTED) { $query = "UPDATE civicrm_contribution SET trxn_id='" . $decodecResponse->Ds_AuthorisationCode . "', contribution_status_id=1 where id='" . self::trimAmount($decodecResponse->Ds_Order) . "'"; CRM_Core_DAO::executeQuery($query); } else { $error = self::trimAmount($decodecResponse->Ds_Response); if (array_key_exists($error, $errors)) { $error = $errors[$error]; } $cancel_date = CRM_Utils_Date::currentDBDate(); $query = "UPDATE civicrm_contribution SET contribution_status_id=3, cancel_reason = '" . $error . "' , cancel_date = '" . $cancel_date . "' where id='" . self::trimAmount($decodecResponse->Ds_Order) . "'"; CRM_Core_DAO::executeQuery($query); } break; case 'event': if ($decodecResponse->Ds_Response == self::REDSYS_RESPONSE_CODE_ACCEPTED) { $query = "UPDATE civicrm_contribution SET trxn_id='" . $decodecResponse->Ds_AuthorisationCode . "', contribution_status_id=1 where id='" . self::trimAmount($decodecResponse->Ds_Order) . "'"; CRM_Core_DAO::executeQuery($query); } else { $error = self::trimAmount($decodecResponse->Ds_Response); if (array_key_exists($error, $errors)) { $error = $errors[$error]; } $cancel_date = CRM_Utils_Date::currentDBDate(); $query = "UPDATE civicrm_contribution SET contribution_status_id=3, cancel_reason = '" . $error . "' , cancel_date = '" . $cancel_date . "' where id='" . self::trimAmount($decodecResponse->Ds_Order) . "'"; CRM_Core_DAO::executeQuery($query); } break; default: require_once 'CRM/Core/Error.php'; CRM_Core_Error::debug_log_message("Could not get module name from request url"); } } }
/** * Update the recurring contribution. * * @param object $current_recur * The ID of the recurring contribution * * @return object * The recurring contribution object. */ function update_recurring_contribution($current_recur) { /* * Creating a new recurrence object as the DAO had problems saving unless all the dates were overwritten. Seems easier to create a new object and only update the fields that are needed @todo - switching to using the api would solve the DAO dates problem & api accepts 'In Progress' so no need to resolve it first. */ $updated_recur = $current_recur; $updated_recur->id = $current_recur->id; $updated_recur->modified_date = CRM_Utils_Date::isoToMysql(date('Y-m-d H:i:s')); $updated_recur->failure_count = $current_recur->failure_count; /* * Update the next date to schedule a contribution. If all installments complete, mark the recurring contribution as complete */ if (_versionAtLeast(4.4)) { $updated_recur->next_sched_contribution_date = CRM_Utils_Date::isoToMysql(date('Y-m-d 00:00:00', strtotime('+' . $current_recur->frequency_interval . ' ' . $current_recur->frequency_unit))); } else { $updated_recur->next_sched_contribution = CRM_Utils_Date::isoToMysql(date('Y-m-d 00:00:00', strtotime('+' . $current_recur->frequency_interval . ' ' . $current_recur->frequency_unit))); } if (isset($current_recur->installments) && $current_recur->installments > 0) { $contributions = new CRM_Contribute_BAO_Contribution(); $contributions->whereAdd("`contribution_recur_id` = " . $current_recur->id); $contributions->find(); if ($contributions->N >= $current_recur->installments) { if (_versionAtLeast(4.4)) { $updated_recur->next_sched_contribution_date = NULL; } else { $updated_recur->next_sched_contribution = NULL; } $updated_recur->contribution_status_id = _eway_recurring_get_contribution_status_id('Completed'); $updated_recur->end_date = CRM_Utils_Date::isoToMysql(date('Y-m-d 00:00:00')); } } return $updated_recur->save(); }
protected function validateValue($rec, $field, $validType, $exceptionCode = 0, $import_type = 'JG') { if (!array_key_exists($field, $rec)) { throw new Exception("Not in record '{$field}'"); } $value = $origValue = $rec[$field]; switch ($validType) { //===== general types ==== case 'money': //matusz: filter out all which does not make a money value invalid $value = str_replace('£', '', $value); $value = str_replace(',', '', $value); $value = trim($value); if (strlen($value) == 0) { // Null entry // throw new CRM_Finance_BAO_Import_ValidateException( // "Can't be empty", // $exceptionCode, // $value); $value = 0; } if (!is_numeric($value)) { throw new CRM_Finance_BAO_Import_ValidateException("Invalid number", $exceptionCode, $value); } $value = floatval($value); //mzeman: we take negative values also as per discussion with psaleh // if ($value < 0) { // throw new CRM_Finance_BAO_Import_ValidateException( // "Value is zero or less", // $exceptionCode, // $value); // } break; case 'contactId': if (empty($value)) { throw new CRM_Finance_BAO_Import_ValidateException("Contact id is empty", self::VALIDATE_ERR_NO_CONTACT, $value); } // Check if the contact exists require_once 'CRM/Contact/DAO/Contact.php'; $contact = new CRM_Contact_DAO_Contact(); $contact->id = $value; $contact->find(); if (!$contact->fetch()) { throw new CRM_Finance_BAO_Import_ValidateException("Can't find contact for this id", self::VALIDATE_ERR_NO_CONTACT, $value); } break; //===== specific types ===== //===== specific types ===== case 'VARef': $value = trim($value); if (empty($value)) { throw new CRM_Finance_BAO_Import_ValidateException("VARef is empty", self::VALIDATE_ERR_REF_EMPTY, $value); } // Check if the contact exists require_once 'CRM/Contact/DAO/Contact.php'; $contact = new CRM_Contact_DAO_Contact(); $contact->external_identifier = $value; $contact->find(); if (!$contact->fetch()) { throw new CRM_Finance_BAO_Import_ValidateException("Can't find contact for this VAReference", self::VALIDATE_ERR_REF_NO_CONTACT, $value); } return array($field => $value, 'contact_id' => $contact->id, 'contact_display_name' => $contact->display_name); break; case 'campaignCode': $value = trim($value); if (empty($value)) { throw new CRM_Finance_BAO_Import_ValidateException("Code invalid", self::VALIDATE_ERR_CAMPAIGN_CODE, $value); } // Check if the contact exists require_once 'CRM/Campaign/BAO/Campaign.php'; $ret = civicrm_api('Campaign', 'GET', array('external_identifier' => $value, 'version' => 3)); if ($ret['is_error'] || $ret['count'] == 0) { throw new CRM_Finance_BAO_Import_ValidateException("Can't find campaign for this code", self::VALIDATE_ERR_CAMPAIGN_CODE, $value); } $id = $ret['id']; $contId = $this->getContributionTypeIdByCampaignId($id); //matusz: TODO check issue here also if its available and assigned to charity return array($field => $value, 'campaign_id' => $id, 'contribution_type_id' => $contId); break; // PS 03/10/2012 // New case to deal with Campaign ID now being passed in by External Sources // PS 03/10/2012 // New case to deal with Campaign ID now being passed in by External Sources case 'campaignID': $value = trim($value); if (empty($value)) { throw new CRM_Finance_BAO_Import_ValidateException("ID invalid", self::VALIDATE_ERR_CAMPAIGN_ID, $value); } // Check if the contact exists require_once 'CRM/Campaign/BAO/Campaign.php'; $ret = civicrm_api('Campaign', 'GET', array('id' => $value, 'version' => 3)); if ($ret['is_error'] || $ret['count'] == 0) { throw new CRM_Finance_BAO_Import_ValidateException("Can't find campaign for this id", self::VALIDATE_ERR_CAMPAIGN_ID, $value); } $id = $ret['id']; $contId = $this->getContributionTypeIdByCampaignId($id); //matusz: TODO check issue here also if its available and assigned to charity return array($field => $value, 'campaign_id' => $id, 'contribution_type_id' => $contId); break; case 'grossAmount': return $this->validateValue($rec, $field, 'money', self::VALIDATE_ERR_GROSSAMOUNT_INVALID); break; case 'netAmount': return $this->validateValue($rec, $field, 'money', self::VALIDATE_ERR_NETAMOUNT_INVALID); break; case 'feeAmount': try { return $this->validateValue($rec, $field, 'money', self::VALIDATE_ERR_FEE_AMOUNT_INVALID); } catch (CRM_Finance_BAO_Import_ValidateException $e) { $rec[$field] = $rec['gross_amount'] - $rec['net_amount']; return $this->validateValue($rec, $field, 'money', self::VALIDATE_ERR_FEE_AMOUNT_INVALID); } break; case 'debit': $debit = $this->validateValue($rec, $field, 'money', self::VALIDATE_ERR_DEBIT_INVALID); return $debit; break; case 'credit': $credit = $this->validateValue($rec, $field, 'money', self::VALIDATE_ERR_CREDIT_INVALID); return array($field => $credit, 'net_amount' => $credit); break; case 'transactionId': require_once 'CRM/Contribute/BAO/Contribution.php'; $contribution = new CRM_Contribute_BAO_Contribution(); $contribution->trxn_id = $value; $found = $contribution->find(true); if ($found) { //mzeman: TODO XXX DEBUG if exists delete so it's valid //$contribution->delete(); //return array(); throw new CRM_Finance_BAO_Import_ValidateException("Transaction ID exists already", self::VALIDATE_ERR_TRANSACTION_EXISTS, $value); } break; case 'donationDate': if (empty($value)) { throw new CRM_Finance_BAO_Import_ValidateException("donationDate is empty", self::VALIDATE_ERR_DONATIONDATE_EMPTY, $value); } $date = DateTime::createFromFormat('Y-m-d', $value); if ($date === false) { throw new CRM_Finance_BAO_Import_ValidateException("donationDate is not in valid format YYYY-MM-DD", self::VALIDATE_ERR_DONATIONDATE_INVALID_FORMAT, $value); } break; case 'paidToCharityDate': if (empty($value)) { throw new CRM_Finance_BAO_Import_ValidateException("paidToCharityDate is empty", self::VALIDATE_ERR_PAIDTOCHARITYDATE_EMPTY, $value); } $date = DateTime::createFromFormat('Y-m-d', $value); if ($date === false) { throw new CRM_Finance_BAO_Import_ValidateException("donationDate is not in valid format YYYY-MM-DD", self::VALIDATE_ERR_PAIDTOCHARITYDATE_INVALID, $value); } break; case 'directDebitRef': return $this->validateDirectTransferRef('DIRECTDEBIT', $value, self::VALIDATE_ERR_DIRECT_DEBIT_REF); break; case 'payrollCTCRef': return $this->validateDirectTransferRef('PAYROLLCTC', $value, self::VALIDATE_ERR_DIRECT_DEBIT_REF); break; case 'financialImportReference': return $this->validateDirectTransferRef($this->directTransferCode, $value, self::VALIDATE_ERR_DIRECT_DEBIT_REF); break; default: throw new Exception("N/I validType: '{$validType}'"); } if ($value !== $origValue) { return array($field => $value); } return array(); }
/** * Send receipt from contribution. * * Do not call this directly - it is being refactored. use contribution.sendmessage api call. * * Note that the compose message part has been moved to contribution * In general LoadObjects is called first to get the objects but the composeMessageArray function now calls it. * * @param array $input * Incoming data from Payment processor. * @param array $ids * Related object IDs. * @param int $contributionID * @param array $values * Values related to objects that have already been loaded. * @param bool $recur * Is it part of a recurring contribution. * @param bool $returnMessageText * Should text be returned instead of sent. This. * is because the function is also used to generate pdfs * * @return array * @throws \CRM_Core_Exception * @throws \CiviCRM_API3_Exception */ public static function sendMail(&$input, &$ids, $contributionID, &$values, $recur = FALSE, $returnMessageText = FALSE) { $input['is_recur'] = $recur; $contribution = new CRM_Contribute_BAO_Contribution(); $contribution->id = $contributionID; if (!$contribution->find(TRUE)) { throw new CRM_Core_Exception('Contribution does not exist'); } $contribution->loadRelatedObjects($input, $ids, TRUE); // set receipt from e-mail and name in value if (!$returnMessageText) { $session = CRM_Core_Session::singleton(); $userID = $session->get('userID'); if (!empty($userID)) { list($userName, $userEmail) = CRM_Contact_BAO_Contact_Location::getEmailDetails($userID); $values['receipt_from_email'] = CRM_Utils_Array::value('receipt_from_email', $input, $userEmail); $values['receipt_from_name'] = CRM_Utils_Array::value('receipt_from_name', $input, $userName); } } $return = $contribution->composeMessageArray($input, $ids, $values, $recur, $returnMessageText); // Contribution ID should really always be set. But ? if (!$returnMessageText && (!isset($input['receipt_update']) || $input['receipt_update']) && empty($contribution->receipt_date)) { civicrm_api3('Contribution', 'create', array('receipt_date' => 'now', 'id' => $contribution->id)); } return $return; }
/** * Set up participant requirements for test. */ public function _setUpParticipantObjects() { $event = $this->eventCreate(array('is_email_confirm' => 1)); $this->assertAPISuccess($event, 'line ' . __LINE__ . ' set-up of event'); $this->_eventId = $event['id']; $this->_participantId = $this->participantCreate(array('event_id' => $this->_eventId, 'contact_id' => $this->_contactId)); //we'll create membership payment here because to make setup more re-usable $participantPayment = civicrm_api('participant_payment', 'create', array('version' => 3, 'contribution_id' => $this->_contributionId, 'participant_id' => $this->_participantId)); $this->assertAPISuccess($participantPayment, 'line ' . __LINE__ . ' set-up of event'); $contribution = new CRM_Contribute_BAO_Contribution(); $contribution->id = $this->_contributionId; $contribution->find(); $this->objects['contribution'] = $contribution; $this->input = array('component' => 'event', 'total_amount' => 150.0, 'invoiceID' => "c8acb91e080ad7bd8a2adc119c192885", 'contactID' => $this->_contactId, 'contributionID' => $contribution->id, 'participantID' => $this->_participantId); $this->ids['participant'] = $this->_participantId; $this->ids['event'] = $this->_eventId; }
/** * Set up participant requirements for test. */ public function _setUpParticipantObjects() { $event = $this->eventCreate(array('is_email_confirm' => 1)); $this->_eventId = $event['id']; $this->_participantId = $this->participantCreate(array('event_id' => $this->_eventId, 'contact_id' => $this->_contactId)); $this->callAPISuccess('participant_payment', 'create', array('contribution_id' => $this->_contributionId, 'participant_id' => $this->_participantId)); $contribution = new CRM_Contribute_BAO_Contribution(); $contribution->id = $this->_contributionId; $contribution->find(); $this->objects['contribution'] = $contribution; $this->input = array('component' => 'event', 'total_amount' => 150.0, 'invoiceID' => "c8acb91e080ad7bd8a2adc119c192885", 'contactID' => $this->_contactId, 'contributionID' => $contribution->id, 'participantID' => $this->_participantId); $this->ids['participant'] = $this->_participantId; $this->ids['event'] = $this->_eventId; }
/** * Complete an existing (pending) transaction, updating related entities (participant, membership, pledge etc) * and taking any complete actions from the contribution page (e.g. send receipt) * * @todo - most of this should live in the BAO layer but as we want it to be an addition * to 4.3 which is already stable we should add it to the api layer & re-factor into the BAO layer later * * @param array $params input parameters * {@getfields Contribution_completetransaction} * @return array Api result array * @static void * @access public * */ function civicrm_api3_contribution_completetransaction(&$params) { $input = $ids = array(); $contribution = new CRM_Contribute_BAO_Contribution(); $contribution->id = $params['id']; $contribution->find(TRUE); if (!$contribution->id == $params['id']) { throw new API_Exception('A valid contribution ID is required', 'invalid_data'); } try { if (!$contribution->loadRelatedObjects($input, $ids, FALSE, TRUE)) { throw new API_Exception('failed to load related objects'); } $objects = $contribution->_relatedObjects; $objects['contribution'] =& $contribution; $input['component'] = $contribution->_component; $input['is_test'] = $contribution->is_test; $input['trxn_id'] = $contribution->trxn_id; $input['amount'] = $contribution->total_amount; if (isset($params['is_email_receipt'])) { $input['is_email_receipt'] = $params['is_email_receipt']; } // @todo required for base ipn but problematic as api layer handles this $transaction = new CRM_Core_Transaction(); $ipn = new CRM_Core_Payment_BaseIPN(); $ipn->completeTransaction($input, $ids, $objects, $transaction); } catch (Exception $e) { throw new API_Exception('failed to load related objects' . $e->getMessage() . "\n" . $e->getTraceAsString()); } }
/** * Function to process the form * * @access public * @return None */ public function postProcess() { require_once 'CRM/Member/BAO/Membership.php'; require_once 'CRM/Member/BAO/MembershipType.php'; require_once 'CRM/Member/BAO/MembershipStatus.php'; if ($this->_action & CRM_Core_Action::DELETE) { CRM_Member_BAO_Membership::deleteRelatedMemberships($this->_id); CRM_Member_BAO_Membership::deleteMembership($this->_id); return; } $config =& CRM_Core_Config::singleton(); // get the submitted form values. $this->_params = $formValues = $this->controller->exportValues($this->_name); $params = array(); $ids = array(); // set the contact, when contact is selected if (CRM_Utils_Array::value('contact_select_id', $formValues)) { $this->_contactID = CRM_Utils_Array::value('contact_select_id', $formValues); } $params['contact_id'] = $this->_contactID; // we need to retrieve email address if ($this->_context == 'standalone' && CRM_Utils_Array::value('send_receipt', $formValues)) { require_once 'CRM/Contact/BAO/Contact/Location.php'; list($this->_contributorDisplayName, $this->_contributorEmail) = CRM_Contact_BAO_Contact_Location::getEmailDetails($this->_contactID); } $fields = array('status_id', 'source', 'is_override'); foreach ($fields as $f) { $params[$f] = CRM_Utils_Array::value($f, $formValues); } // fix for CRM-3724 // when is_override false ignore is_admin statuses during membership // status calculation. similarly we did fix for import in CRM-3570. if (!CRM_Utils_Array::value('is_override', $params)) { $params['exclude_is_admin'] = true; } $params['membership_type_id'] = $formValues['membership_type_id'][1]; $joinDate = CRM_Utils_Date::processDate($formValues['join_date']); $startDate = CRM_Utils_Date::processDate($formValues['start_date']); $endDate = CRM_Utils_Date::processDate($formValues['end_date']); $calcDates = CRM_Member_BAO_MembershipType::getDatesForMembershipType($params['membership_type_id'], $joinDate, $startDate, $endDate); $dates = array('join_date', 'start_date', 'end_date', 'reminder_date', 'receive_date'); $currentTime = getDate(); foreach ($dates as $d) { if (isset($formValues[$d]) && !CRM_Utils_System::isNull($formValues[$d])) { $params[$d] = CRM_Utils_Date::processDate($formValues[$d]); } else { if (isset($calcDates[$d])) { $params[$d] = CRM_Utils_Date::processDate($calcDates[$d]); } } } if ($this->_id) { $ids['membership'] = $params['id'] = $this->_id; } $session = CRM_Core_Session::singleton(); $ids['userId'] = $session->get('userID'); // membership type custom data $customFields = CRM_Core_BAO_CustomField::getFields('Membership', false, false, CRM_Utils_Array::value('membership_type_id', $params)); $customFields = CRM_Utils_Array::crmArrayMerge($customFields, CRM_Core_BAO_CustomField::getFields('Membership', false, false, null, null, true)); $params['custom'] = CRM_Core_BAO_CustomField::postProcess($formValues, $customFields, $this->_id, 'Membership'); // Retrieve the name and email of the current user - this will be the FROM for the receipt email require_once 'CRM/Contact/BAO/Contact/Location.php'; list($userName, $userEmail) = CRM_Contact_BAO_Contact_Location::getEmailDetails($ids['userId']); if (CRM_Utils_Array::value('record_contribution', $formValues)) { $recordContribution = array('total_amount', 'contribution_type_id', 'payment_instrument_id', 'trxn_id', 'contribution_status_id', 'check_number'); foreach ($recordContribution as $f) { $params[$f] = CRM_Utils_Array::value($f, $formValues); } $membershipType = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipType', $formValues['membership_type_id'][1]); if (!$this->_onlinePendingContributionId) { $params['contribution_source'] = "{$membershipType} Membership: Offline membership signup (by {$userName})"; } if ($formValues['send_receipt']) { $params['receipt_date'] = $params['receive_date']; } //insert contribution type name in receipt. $formValues['contributionType_name'] = CRM_Core_DAO::getFieldValue('CRM_Contribute_DAO_ContributionType', $formValues['contribution_type_id']); } if ($this->_mode) { $params['total_amount'] = $formValues['total_amount'] = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipType', $params['membership_type_id'], 'minimum_fee'); $params['contribution_type_id'] = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipType', $params['membership_type_id'], 'contribution_type_id'); require_once 'CRM/Core/BAO/PaymentProcessor.php'; $this->_paymentProcessor = CRM_Core_BAO_PaymentProcessor::getPayment($formValues['payment_processor_id'], $this->_mode); require_once "CRM/Contact/BAO/Contact.php"; $now = date('YmdHis'); $fields = array(); // set email for primary location. $fields["email-Primary"] = 1; $formValues["email-5"] = $formValues["email-Primary"] = $this->_contributorEmail; $params['register_date'] = $now; // now set the values for the billing location. foreach ($this->_fields as $name => $dontCare) { $fields[$name] = 1; } // also add location name to the array $formValues["address_name-{$this->_bltID}"] = CRM_Utils_Array::value('billing_first_name', $formValues) . ' ' . CRM_Utils_Array::value('billing_middle_name', $formValues) . ' ' . CRM_Utils_Array::value('billing_last_name', $formValues); $formValues["address_name-{$this->_bltID}"] = trim($formValues["address_name-{$this->_bltID}"]); $fields["address_name-{$this->_bltID}"] = 1; $fields["email-{$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}", $formValues)) { $formValues[$name] = $formValues["billing_{$name}"]; $formValues['preserveDBName'] = true; } } $contactID = CRM_Contact_BAO_Contact::createProfileContact($formValues, $fields, $this->_contactID, null, null, $ctype); // add all the additioanl payment params we need $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}"]); $this->_params["country-{$this->_bltID}"] = $this->_params["billing_country-{$this->_bltID}"] = CRM_Core_PseudoConstant::countryIsoCode($this->_params["billing_country_id-{$this->_bltID}"]); $this->_params['year'] = $this->_params['credit_card_exp_date']['Y']; $this->_params['month'] = $this->_params['credit_card_exp_date']['M']; $this->_params['ip_address'] = CRM_Utils_System::ipAddress(); $this->_params['amount'] = $params['total_amount']; $this->_params['currencyID'] = $config->defaultCurrency; $this->_params['payment_action'] = 'Sale'; $this->_params['invoiceID'] = md5(uniqid(rand(), 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; if (CRM_Utils_Array::value('send_receipt', $this->_params)) { $paymentParams['email'] = $this->_contributorEmail; } require_once 'CRM/Core/Payment/Form.php'; CRM_Core_Payment_Form::mapParams($this->_bltID, $this->_params, $paymentParams, true); $payment =& CRM_Core_Payment::singleton($this->_mode, 'Contribute', $this->_paymentProcessor, $this); $result =& $payment->doDirectPayment($paymentParams); if (is_a($result, 'CRM_Core_Error')) { CRM_Core_Error::displaySessionError($result); CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/contact/view/membership', "reset=1&action=add&cid={$this->_contactID}&context=&mode={$this->_mode}")); } if ($result) { $this->_params = array_merge($this->_params, $result); } $params['contribution_status_id'] = 1; $params['receive_date'] = $now; $params['invoice_id'] = $this->_params['invoiceID']; $params['contribution_source'] = ts('Online Membership: Admin Interface'); $params['source'] = $formValues['source'] ? $formValues['source'] : $params['contribution_source']; $params['trxn_id'] = $result['trxn_id']; $params['payment_instrument_id'] = 1; $params['is_test'] = $this->_mode == 'live' ? 0 : 1; if (CRM_Utils_Array::value('send_receipt', $this->_params)) { $params['receipt_date'] = $now; } else { $params['receipt_date'] = null; } $this->set('params', $this->_params); $this->assign('trxn_id', $result['trxn_id']); $this->assign('receive_date', CRM_Utils_Date::mysqlToIso($params['receive_date'])); // required for creating membership for related contacts $params['action'] = $this->_action; $membership =& CRM_Member_BAO_Membership::create($params, $ids); $contribution = new CRM_Contribute_BAO_Contribution(); $contribution->trxn_id = $result['trxn_id']; if ($contribution->find(true)) { // next create the transaction record $trxnParams = array('contribution_id' => $contribution->id, 'trxn_date' => $now, 'trxn_type' => 'Debit', 'total_amount' => $params['total_amount'], 'fee_amount' => CRM_Utils_Array::value('fee_amount', $result), 'net_amount' => CRM_Utils_Array::value('net_amount', $result, $params['total_amount']), 'currency' => $config->defaultCurrency, 'payment_processor' => $this->_paymentProcessor['payment_processor_type'], 'trxn_id' => $result['trxn_id']); require_once 'CRM/Contribute/BAO/FinancialTrxn.php'; $trxn =& CRM_Contribute_BAO_FinancialTrxn::create($trxnParams); } } else { $params['action'] = $this->_action; if ($this->_onlinePendingContributionId && CRM_Utils_Array::value('record_contribution', $formValues)) { // update membership as well as contribution object, CRM-4395 require_once 'CRM/Contribute/Form/Contribution.php'; $params['contribution_id'] = $this->_onlinePendingContributionId; $params['componentId'] = $params['id']; $params['componentName'] = 'contribute'; $result = CRM_Contribute_BAO_Contribution::transitionComponents($params, true); //carry updated membership object. $membership =& new CRM_Member_DAO_Membership(); $membership->id = $this->_id; $membership->find(true); $cancelled = true; if ($membership->end_date) { //display end date w/ status message. $endDate = $membership->end_date; require_once 'CRM/Member/PseudoConstant.php'; $membershipStatues = CRM_Member_PseudoConstant::membershipStatus(); if (!in_array($membership->status_id, array(array_search('Cancelled', $membershipStatues), array_search('Expired', $membershipStatues)))) { $cancelled = false; } } // suppress form values in template. $this->assign('cancelled', $cancelled); } else { $membership =& CRM_Member_BAO_Membership::create($params, $ids); } } if (CRM_Utils_Array::value('send_receipt', $formValues)) { require_once 'CRM/Core/DAO.php'; CRM_Core_DAO::setFieldValue('CRM_Member_DAO_MembershipType', $params['membership_type_id'], 'receipt_text_signup', $formValues['receipt_text_signup']); } $receiptSend = false; if (CRM_Utils_Array::value('send_receipt', $formValues)) { $receiptSend = true; $receiptFrom = "{$userName} <{$userEmail}>"; if (CRM_Utils_Array::value('payment_instrument_id', $formValues)) { $paymentInstrument = CRM_Contribute_PseudoConstant::paymentInstrument(); $formValues['paidBy'] = $paymentInstrument[$formValues['payment_instrument_id']]; } // retrieve custom data require_once "CRM/Core/BAO/UFGroup.php"; $customFields = $customValues = array(); foreach ($this->_groupTree as $groupID => $group) { if ($groupID == 'info') { continue; } foreach ($group['fields'] as $k => $field) { $field['title'] = $field['label']; $customFields["custom_{$k}"] = $field; } } $members = array(array('member_id', '=', $membership->id, 0, 0)); // check whether its a test drive if ($this->_mode) { $members[] = array('member_test', '=', 1, 0, 0); } CRM_Core_BAO_UFGroup::getValues($this->_contactID, $customFields, $customValues, false, $members); if ($this->_mode) { if (CRM_Utils_Array::value('billing_first_name', $this->_params)) { $name = $this->_params['billing_first_name']; } if (CRM_Utils_Array::value('billing_middle_name', $this->_params)) { $name .= " {$this->_params['billing_middle_name']}"; } if (CRM_Utils_Array::value('billing_last_name', $this->_params)) { $name .= " {$this->_params['billing_last_name']}"; } $this->assign('billingName', $name); // assign the address formatted up for display $addressParts = array("street_address-{$this->_bltID}", "city-{$this->_bltID}", "postal_code-{$this->_bltID}", "state_province-{$this->_bltID}", "country-{$this->_bltID}"); $addressFields = array(); foreach ($addressParts as $part) { list($n, $id) = explode('-', $part); if (isset($this->_params['billing_' . $part])) { $addressFields[$n] = $this->_params['billing_' . $part]; } } require_once 'CRM/Utils/Address.php'; $this->assign('address', CRM_Utils_Address::format($addressFields)); $date = CRM_Utils_Date::format($this->_params['credit_card_exp_date']); $date = CRM_Utils_Date::mysqlToIso($date); $this->assign('credit_card_exp_date', $date); $this->assign('credit_card_number', CRM_Utils_System::mungeCreditCard($this->_params['credit_card_number'])); $this->assign('credit_card_type', $this->_params['credit_card_type']); $this->assign('contributeMode', 'direct'); $this->assign('isAmountzero', 0); $this->assign('is_pay_later', 0); $this->assign('isPrimary', 1); } $this->assign('module', 'Membership'); $this->assign('contactID', $this->_contactID); $this->assign('membershipID', $params['membership_id']); $this->assign('receiptType', 'membership signup'); $this->assign('receive_date', $params['receive_date']); $this->assign('formValues', $formValues); $this->assign('mem_start_date', CRM_Utils_Date::customFormat($calcDates['start_date'])); $this->assign('mem_end_date', CRM_Utils_Date::customFormat($calcDates['end_date'])); $this->assign('membership_name', CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipType', $formValues['membership_type_id'][1])); $this->assign('customValues', $customValues); require_once 'CRM/Core/BAO/MessageTemplates.php'; list($mailSend, $subject, $message, $html) = CRM_Core_BAO_MessageTemplates::sendTemplate(array('groupName' => 'msg_tpl_workflow_contribution', 'valueName' => 'contribution_offline_receipt', 'contactId' => $this->_contactID, 'from' => $receiptFrom, 'toName' => $this->_contributorDisplayName, 'toEmail' => $this->_contributorEmail, 'isTest' => (bool) ($this->_action & CRM_Core_Action::PREVIEW))); } if ($this->_action & CRM_Core_Action::UPDATE) { $statusMsg = ts('Membership for %1 has been updated.', array(1 => $this->_contributorDisplayName)); if ($endDate) { $endDate = CRM_Utils_Date::customFormat($endDate); $statusMsg .= ' ' . ts('The membership End Date is %1.', array(1 => $endDate)); } if ($receiptSend) { $statusMsg .= ' ' . ts('A confirmation and receipt has been sent to %1.', array(1 => $this->_contributorEmail)); } } elseif ($this->_action & CRM_Core_Action::ADD) { require_once 'CRM/Core/DAO.php'; $memType = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipType', $params['membership_type_id']); $statusMsg = ts('%1 membership for %2 has been added.', array(1 => $memType, 2 => $this->_contributorDisplayName)); //get the end date from calculated dates. $endDate = $endDate ? $endDate : CRM_Utils_Array::value('end_date', $calcDates); if ($endDate) { $endDate = CRM_Utils_Date::customFormat($endDate); $statusMsg .= ' ' . ts('The new membership End Date is %1.', array(1 => $endDate)); } if ($receiptSend && $mailSend) { $statusMsg .= ' ' . ts('A membership confirmation and receipt has been sent to %1.', array(1 => $this->_contributorEmail)); } } CRM_Core_Session::setStatus($statusMsg); $buttonName = $this->controller->getButtonName(); if ($this->_context == 'standalone') { if ($buttonName == $this->getButtonName('upload', 'new')) { $session->replaceUserContext(CRM_Utils_System::url('civicrm/member/add', 'reset=1&action=add&context=standalone')); } else { $session->replaceUserContext(CRM_Utils_System::url('civicrm/contact/view', "reset=1&cid={$this->_contactID}&selectedChild=member")); } } else { if ($buttonName == $this->getButtonName('upload', 'new')) { $session->replaceUserContext(CRM_Utils_System::url('civicrm/contact/view/membership', "reset=1&action=add&context=membership&cid={$this->_contactID}")); } } }
/** * Process the form submission. * * * @return void */ public function postProcess() { if ($this->_action & CRM_Core_Action::DELETE) { CRM_Member_BAO_Membership::del($this->_id); return; } $allMemberStatus = CRM_Member_PseudoConstant::membershipStatus(); $allContributionStatus = CRM_Contribute_PseudoConstant::contributionStatus(); $isTest = $this->_mode == 'test' ? 1 : 0; $lineItems = NULL; if (!empty($this->_lineItem)) { $lineItems = $this->_lineItem; } $config = CRM_Core_Config::singleton(); // get the submitted form values. $this->_params = $formValues = $this->controller->exportValues($this->_name); $this->convertDateFieldsToMySQL($formValues); $params = $softParams = $ids = array(); $membershipTypeValues = array(); foreach ($this->_memTypeSelected as $memType) { $membershipTypeValues[$memType]['membership_type_id'] = $memType; } //take the required membership recur values. if ($this->_mode && !empty($this->_params['auto_renew'])) { $params['is_recur'] = $this->_params['is_recur'] = $formValues['is_recur'] = TRUE; $mapping = array('frequency_interval' => 'duration_interval', 'frequency_unit' => 'duration_unit'); $count = 0; foreach ($this->_memTypeSelected as $memType) { $recurMembershipTypeValues = CRM_Utils_Array::value($memType, $this->_recurMembershipTypes, array()); foreach ($mapping as $mapVal => $mapParam) { $membershipTypeValues[$memType][$mapVal] = CRM_Utils_Array::value($mapParam, $recurMembershipTypeValues); if (!$count) { $this->_params[$mapVal] = $formValues[$mapVal] = CRM_Utils_Array::value($mapParam, $recurMembershipTypeValues); } } $count++; } } // process price set and get total amount and line items. $lineItem = array(); $priceSetId = NULL; if (!($priceSetId = CRM_Utils_Array::value('price_set_id', $formValues))) { CRM_Member_BAO_Membership::createLineItems($this, $formValues['membership_type_id'], $priceSetId); } $isQuickConfig = 0; if ($this->_priceSetId && CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceSet', $this->_priceSetId, 'is_quick_config')) { $isQuickConfig = 1; } $termsByType = array(); if ($priceSetId) { CRM_Price_BAO_PriceSet::processAmount($this->_priceSet['fields'], $this->_params, $lineItem[$priceSetId]); if (CRM_Utils_Array::value('tax_amount', $this->_params)) { $params['tax_amount'] = $this->_params['tax_amount']; } $params['total_amount'] = CRM_Utils_Array::value('amount', $this->_params); $submittedFinancialType = CRM_Utils_Array::value('financial_type_id', $formValues); if (!empty($lineItem[$priceSetId])) { foreach ($lineItem[$priceSetId] as &$li) { if (!empty($li['membership_type_id'])) { if (!empty($li['membership_num_terms'])) { $termsByType[$li['membership_type_id']] = $li['membership_num_terms']; } } ///CRM-11529 for quick config backoffice transactions //when financial_type_id is passed in form, update the //lineitems with the financial type selected in form if ($isQuickConfig && $submittedFinancialType) { $li['financial_type_id'] = $submittedFinancialType; } } } } $this->storeContactFields($formValues); $params['contact_id'] = $this->_contactID; $fields = array('status_id', 'source', 'is_override', 'campaign_id'); foreach ($fields as $f) { $params[$f] = CRM_Utils_Array::value($f, $formValues); } // fix for CRM-3724 // when is_override false ignore is_admin statuses during membership // status calculation. similarly we did fix for import in CRM-3570. if (empty($params['is_override'])) { $params['exclude_is_admin'] = TRUE; } // process date params to mysql date format. $dateTypes = array('join_date' => 'joinDate', 'start_date' => 'startDate', 'end_date' => 'endDate'); foreach ($dateTypes as $dateField => $dateVariable) { ${$dateVariable} = CRM_Utils_Date::processDate($formValues[$dateField]); } $memTypeNumTerms = empty($termsByType) ? CRM_Utils_Array::value('num_terms', $formValues) : NULL; $calcDates = array(); foreach ($this->_memTypeSelected as $memType) { if (empty($memTypeNumTerms)) { $memTypeNumTerms = CRM_Utils_Array::value($memType, $termsByType, 1); } $calcDates[$memType] = CRM_Member_BAO_MembershipType::getDatesForMembershipType($memType, $joinDate, $startDate, $endDate, $memTypeNumTerms); } foreach ($calcDates as $memType => $calcDate) { foreach (array_keys($dateTypes) as $d) { //first give priority to form values then calDates. $date = CRM_Utils_Array::value($d, $formValues); if (!$date) { $date = CRM_Utils_Array::value($d, $calcDate); } $membershipTypeValues[$memType][$d] = CRM_Utils_Date::processDate($date); //$params[$d] = CRM_Utils_Date::processDate( $date ); } } // max related memberships - take from form or inherit from membership type foreach ($this->_memTypeSelected as $memType) { if (array_key_exists('max_related', $formValues)) { $membershipTypeValues[$memType]['max_related'] = CRM_Utils_Array::value('max_related', $formValues); } } if ($this->_id) { $ids['membership'] = $params['id'] = $this->_id; } $session = CRM_Core_Session::singleton(); $ids['userId'] = $session->get('userID'); // membership type custom data foreach ($this->_memTypeSelected as $memType) { $customFields = CRM_Core_BAO_CustomField::getFields('Membership', FALSE, FALSE, $memType); $customFields = CRM_Utils_Array::crmArrayMerge($customFields, CRM_Core_BAO_CustomField::getFields('Membership', FALSE, FALSE, NULL, NULL, TRUE)); $membershipTypeValues[$memType]['custom'] = CRM_Core_BAO_CustomField::postProcess($formValues, $customFields, $this->_id, 'Membership'); } foreach ($this->_memTypeSelected as $memType) { $membershipTypes[$memType] = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipType', $memType); } $membershipType = implode(', ', $membershipTypes); // Retrieve the name and email of the current user - this will be the FROM for the receipt email list($userName, $userEmail) = CRM_Contact_BAO_Contact_Location::getEmailDetails($ids['userId']); //CRM-13981, allow different person as a soft-contributor of chosen type if ($this->_contributorContactID != $this->_contactID) { $params['contribution_contact_id'] = $this->_contributorContactID; if (!empty($this->_params['soft_credit_type_id'])) { $softParams['soft_credit_type_id'] = $this->_params['soft_credit_type_id']; $softParams['contact_id'] = $this->_contactID; } } if (!empty($formValues['record_contribution'])) { $recordContribution = array('total_amount', 'financial_type_id', 'payment_instrument_id', 'trxn_id', 'contribution_status_id', 'check_number', 'campaign_id', 'receive_date'); foreach ($recordContribution as $f) { $params[$f] = CRM_Utils_Array::value($f, $formValues); } if (!$this->_onlinePendingContributionId) { if (empty($formValues['source'])) { $params['contribution_source'] = ts('%1 Membership: Offline signup (by %2)', array(1 => $membershipType, 2 => $userName)); } else { $params['contribution_source'] = $formValues['source']; } } if (empty($params['is_override']) && CRM_Utils_Array::value('contribution_status_id', $params) == array_search('Pending', CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name'))) { $params['status_id'] = array_search('Pending', $allMemberStatus); $params['skipStatusCal'] = TRUE; $params['is_pay_later'] = 1; $this->assign('is_pay_later', 1); } if (!empty($formValues['send_receipt'])) { $params['receipt_date'] = CRM_Utils_Array::value('receive_date', $formValues); } //insert financial type name in receipt. $formValues['contributionType_name'] = CRM_Core_DAO::getFieldValue('CRM_Financial_DAO_FinancialType', $formValues['financial_type_id']); } // process line items, until no previous line items. if (!empty($lineItem)) { $params['lineItems'] = $lineItem; $params['processPriceSet'] = TRUE; } $createdMemberships = array(); if ($this->_mode) { if (empty($formValues['total_amount']) && !$priceSetId) { // if total amount not provided minimum for membership type is used $params['total_amount'] = $formValues['total_amount'] = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipType', $formValues['membership_type_id'][1], 'minimum_fee'); } else { $params['total_amount'] = CRM_Utils_Array::value('total_amount', $formValues, 0); } if ($priceSetId && !$isQuickConfig) { $params['financial_type_id'] = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceSet', $priceSetId, 'financial_type_id'); } else { $params['financial_type_id'] = CRM_Utils_Array::value('financial_type_id', $formValues); } $this->_paymentProcessor = CRM_Financial_BAO_PaymentProcessor::getPayment($formValues['payment_processor_id'], $this->_mode); //get the payment processor id as per mode. $params['payment_processor_id'] = $this->_params['payment_processor_id'] = $formValues['payment_processor_id'] = $this->_paymentProcessor['id']; $now = date('YmdHis'); $fields = array(); // set email for primary location. $fields['email-Primary'] = 1; $formValues['email-5'] = $formValues['email-Primary'] = $this->_memberEmail; $params['register_date'] = $now; // now set the values for the billing location. foreach ($this->_fields as $name => $dontCare) { $fields[$name] = 1; } // also add location name to the array $formValues["address_name-{$this->_bltID}"] = CRM_Utils_Array::value('billing_first_name', $formValues) . ' ' . CRM_Utils_Array::value('billing_middle_name', $formValues) . ' ' . CRM_Utils_Array::value('billing_last_name', $formValues); $formValues["address_name-{$this->_bltID}"] = trim($formValues["address_name-{$this->_bltID}"]); $fields["address_name-{$this->_bltID}"] = 1; //ensure we don't over-write the payer's email with the member's email if ($this->_contributorContactID == $this->_contactID) { $fields["email-{$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}", $formValues)) { $formValues[$name] = $formValues["billing_{$name}"]; $formValues['preserveDBName'] = TRUE; } } if ($this->_contributorContactID == $this->_contactID) { //see CRM-12869 for discussion of why we don't do this for separate payee payments CRM_Contact_BAO_Contact::createProfileContact($formValues, $fields, $this->_contributorContactID, NULL, NULL, $ctype); } // add all the additional payment params we need $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}"]); $this->_params["country-{$this->_bltID}"] = $this->_params["billing_country-{$this->_bltID}"] = CRM_Core_PseudoConstant::countryIsoCode($this->_params["billing_country_id-{$this->_bltID}"]); $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'] = $params['total_amount']; $this->_params['currencyID'] = $config->defaultCurrency; $this->_params['description'] = ts('Office Credit Card Membership Signup Contribution'); $this->_params['payment_action'] = 'Sale'; $this->_params['invoiceID'] = md5(uniqid(rand(), TRUE)); $this->_params['financial_type_id'] = $params['financial_type_id']; // 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->_contributorContactID; //CRM-10377 if payment is by an alternate contact then we need to set that person // as the contact in the payment params if ($this->_contributorContactID != $this->_contactID) { if (!empty($this->_params['soft_credit_type_id'])) { $softParams['contact_id'] = $params['contact_id']; $softParams['soft_credit_type_id'] = $this->_params['soft_credit_type_id']; } } if (!empty($this->_params['send_receipt'])) { $paymentParams['email'] = $this->_contributorEmail; } CRM_Core_Payment_Form::mapParams($this->_bltID, $this->_params, $paymentParams, TRUE); // CRM-7137 -for recurring membership, // we do need contribution and recuring records. $result = NULL; if (!empty($paymentParams['is_recur'])) { $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'); } $contribution = CRM_Contribute_Form_Contribution_Confirm::processContribution($this, $paymentParams, $result, $this->_contributorContactID, $contributionType, TRUE, FALSE, $isTest, $lineItems); //create new soft-credit record, CRM-13981 if ($softParams) { $softParams['contribution_id'] = $contribution->id; $softParams['currency'] = $contribution->currency; $softParams['amount'] = $contribution->total_amount; CRM_Contribute_BAO_ContributionSoft::add($softParams); } $paymentParams['contactID'] = $this->_contactID; $paymentParams['contributionID'] = $contribution->id; $paymentParams['contributionTypeID'] = $contribution->financial_type_id; $paymentParams['contributionPageID'] = $contribution->contribution_page_id; $paymentParams['contributionRecurID'] = $contribution->contribution_recur_id; $ids['contribution'] = $contribution->id; $params['contribution_recur_id'] = $paymentParams['contributionRecurID']; } if ($params['total_amount'] > 0.0) { $payment = CRM_Core_Payment::singleton($this->_mode, $this->_paymentProcessor, $this); $result = $payment->doDirectPayment($paymentParams); } if (is_a($result, 'CRM_Core_Error')) { //make sure to cleanup db for recurring case. if (!empty($paymentParams['contributionID'])) { CRM_Contribute_BAO_Contribution::deleteContribution($paymentParams['contributionID']); } if (!empty($paymentParams['contributionRecurID'])) { CRM_Contribute_BAO_ContributionRecur::deleteRecurContribution($paymentParams['contributionRecurID']); } CRM_Core_Error::displaySessionError($result); CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/contact/view/membership', "reset=1&action=add&cid={$this->_contactID}&context=&mode={$this->_mode}")); } if ($result) { $this->_params = array_merge($this->_params, $result); //assign amount to template if payment was successful $this->assign('amount', $params['total_amount']); } // if the payment processor returns a contribution_status_id -> use it! if (isset($result['contribution_status_id'])) { $result['payment_status_id'] = $result['contribution_status_id']; } if (isset($result['payment_status_id'])) { // CRM-16737 $result['contribution_status_id'] is deprecated in favour // of payment_status_id as the payment processor only knows whether the payment is complete // not whether payment completes the contribution $params['contribution_status_id'] = $result['payment_status_id']; } else { $params['contribution_status_id'] = !empty($paymentParams['is_recur']) ? 2 : 1; } if ($params['contribution_status_id'] != array_search('Completed', $allContributionStatus)) { $params['status_id'] = array_search('Pending', $allMemberStatus); $params['skipStatusCal'] = TRUE; // unset send-receipt option, since receipt will be sent when ipn is received. unset($this->_params['send_receipt'], $formValues['send_receipt']); //as membership is pending set dates to null. $memberDates = array('join_date' => 'joinDate', 'start_date' => 'startDate', 'end_date' => 'endDate'); foreach ($memberDates as $dp => $dv) { ${$dv} = NULL; foreach ($this->_memTypeSelected as $memType) { $membershipTypeValues[$memType][$dv] = NULL; } } } $params['receive_date'] = $now; $params['invoice_id'] = $this->_params['invoiceID']; $params['contribution_source'] = ts('%1 Membership Signup: Credit card or direct debit (by %2)', array(1 => $membershipType, 2 => $userName)); $params['source'] = $formValues['source'] ? $formValues['source'] : $params['contribution_source']; $params['trxn_id'] = CRM_Utils_Array::value('trxn_id', $result); $params['payment_instrument_id'] = 1; $params['is_test'] = $this->_mode == 'live' ? 0 : 1; if (!empty($this->_params['send_receipt'])) { $params['receipt_date'] = $now; } else { $params['receipt_date'] = NULL; } $this->set('params', $this->_params); $this->assign('trxn_id', CRM_Utils_Array::value('trxn_id', $result)); $this->assign('receive_date', CRM_Utils_Date::mysqlToIso($params['receive_date'])); // required for creating membership for related contacts $params['action'] = $this->_action; //create membership record. $count = 0; foreach ($this->_memTypeSelected as $memType) { if ($count && ($relateContribution = CRM_Member_BAO_Membership::getMembershipContributionId($membership->id))) { $membershipTypeValues[$memType]['relate_contribution_id'] = $relateContribution; } $membershipParams = array_merge($membershipTypeValues[$memType], $params); //CRM-15366 if (!empty($softParams) && empty($paymentParams['is_recur'])) { $membershipParams['soft_credit'] = $softParams; } if (!empty($paymentParams['is_recur']) && CRM_Utils_Array::value('payment_status_id', $result) == 1) { // CRM-16993 we have a situation where line items have already been created. unset($membershipParams['lineItems']); } $membership = CRM_Member_BAO_Membership::create($membershipParams, $ids); $params['contribution'] = CRM_Utils_Array::value('contribution', $membershipParams); unset($params['lineItems']); $this->_membershipIDs[] = $membership->id; $createdMemberships[$memType] = $membership; $count++; } } else { $params['action'] = $this->_action; if ($this->_onlinePendingContributionId && !empty($formValues['record_contribution'])) { // update membership as well as contribution object, CRM-4395 $params['contribution_id'] = $this->_onlinePendingContributionId; $params['componentId'] = $params['id']; $params['componentName'] = 'contribute'; $result = CRM_Contribute_BAO_Contribution::transitionComponents($params, TRUE); if (!empty($result) && !empty($params['contribution_id'])) { $lineItem = array(); $lineItems = CRM_Price_BAO_LineItem::getLineItems($params['contribution_id'], 'contribution', NULL, TRUE, TRUE); $itemId = key($lineItems); $priceSetId = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceField', $lineItems[$itemId]['price_field_id'], 'price_set_id'); $fieldType = NULL; if ($itemId && !empty($lineItems[$itemId]['price_field_id'])) { $fieldType = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceField', $lineItems[$itemId]['price_field_id'], 'html_type'); } $lineItems[$itemId]['unit_price'] = $params['total_amount']; $lineItems[$itemId]['line_total'] = $params['total_amount']; $lineItems[$itemId]['id'] = $itemId; $lineItem[$priceSetId] = $lineItems; $contributionBAO = new CRM_Contribute_BAO_Contribution(); $contributionBAO->id = $params['contribution_id']; $contributionBAO->contact_id = $params['contact_id']; $contributionBAO->find(); CRM_Price_BAO_LineItem::processPriceSet($params['contribution_id'], $lineItem, $contributionBAO, 'civicrm_membership'); //create new soft-credit record, CRM-13981 if ($softParams) { $softParams['contribution_id'] = $params['contribution_id']; while ($contributionBAO->fetch()) { $softParams['currency'] = $contributionBAO->currency; $softParams['amount'] = $contributionBAO->total_amount; } CRM_Contribute_BAO_ContributionSoft::add($softParams); } } //carry updated membership object. $membership = new CRM_Member_DAO_Membership(); $membership->id = $this->_id; $membership->find(TRUE); $cancelled = TRUE; if ($membership->end_date) { //display end date w/ status message. $endDate = $membership->end_date; if (!in_array($membership->status_id, array(array_search('Cancelled', CRM_Member_PseudoConstant::membershipStatus(NULL, " name = 'Cancelled' ", 'name', FALSE, TRUE)), array_search('Expired', CRM_Member_PseudoConstant::membershipStatus())))) { $cancelled = FALSE; } } // suppress form values in template. $this->assign('cancelled', $cancelled); // FIX ME: need to recheck this // here we might updated dates, so get from object. foreach ($calcDates[$membership->membership_type_id] as $date => &$val) { if ($membership->{$date}) { $val = $membership->{$date}; } } $createdMemberships[] = $membership; } else { $count = 0; foreach ($this->_memTypeSelected as $memType) { if ($count && !empty($formValues['record_contribution']) && ($relateContribution = CRM_Member_BAO_Membership::getMembershipContributionId($membership->id))) { $membershipTypeValues[$memType]['relate_contribution_id'] = $relateContribution; } $membershipParams = array_merge($params, $membershipTypeValues[$memType]); if (!empty($formValues['int_amount'])) { $init_amount = array(); foreach ($formValues as $key => $value) { if (strstr($key, 'txt-price')) { $init_amount[$key] = $value; } } $membershipParams['init_amount'] = $init_amount; } if (!empty($softParams)) { $membershipParams['soft_credit'] = $softParams; } $membership = CRM_Member_BAO_Membership::create($membershipParams, $ids); $params['contribution'] = CRM_Utils_Array::value('contribution', $membershipParams); unset($params['lineItems']); $this->_membershipIDs[] = $membership->id; $createdMemberships[$memType] = $membership; $count++; } } } if (!empty($lineItem[$priceSetId])) { $invoiceSettings = CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::CONTRIBUTE_PREFERENCES_NAME, 'contribution_invoice_settings'); $invoicing = CRM_Utils_Array::value('invoicing', $invoiceSettings); $taxAmount = FALSE; $totalTaxAmount = 0; foreach ($lineItem[$priceSetId] as &$priceFieldOp) { if (!empty($priceFieldOp['membership_type_id'])) { $priceFieldOp['start_date'] = $membershipTypeValues[$priceFieldOp['membership_type_id']]['start_date'] ? CRM_Utils_Date::customFormat($membershipTypeValues[$priceFieldOp['membership_type_id']]['start_date'], '%B %E%f, %Y') : '-'; $priceFieldOp['end_date'] = $membershipTypeValues[$priceFieldOp['membership_type_id']]['end_date'] ? CRM_Utils_Date::customFormat($membershipTypeValues[$priceFieldOp['membership_type_id']]['end_date'], '%B %E%f, %Y') : '-'; } else { $priceFieldOp['start_date'] = $priceFieldOp['end_date'] = 'N/A'; } if ($invoicing && isset($priceFieldOp['tax_amount'])) { $taxAmount = TRUE; $totalTaxAmount += $priceFieldOp['tax_amount']; } } if ($invoicing) { $dataArray = array(); foreach ($lineItem[$priceSetId] as $key => $value) { if (isset($value['tax_amount']) && isset($value['tax_rate'])) { if (isset($dataArray[$value['tax_rate']])) { $dataArray[$value['tax_rate']] = $dataArray[$value['tax_rate']] + CRM_Utils_Array::value('tax_amount', $value); } else { $dataArray[$value['tax_rate']] = CRM_Utils_Array::value('tax_amount', $value); } } } if ($taxAmount) { $this->assign('totalTaxAmount', $totalTaxAmount); $this->assign('taxTerm', CRM_Utils_Array::value('tax_term', $invoiceSettings)); } $this->assign('dataArray', $dataArray); } } $this->assign('lineItem', !empty($lineItem) && !$isQuickConfig ? $lineItem : FALSE); $receiptSend = FALSE; $contributionId = CRM_Member_BAO_Membership::getMembershipContributionId($membership->id); $membershipIds = $this->_membershipIDs; if ($contributionId && !empty($membershipIds)) { $contributionDetails = CRM_Contribute_BAO_Contribution::getContributionDetails(CRM_Export_Form_Select::MEMBER_EXPORT, $this->_membershipIDs); if ($contributionDetails[$membership->id]['contribution_status'] == 'Completed') { $receiptSend = TRUE; } } if (!empty($formValues['send_receipt']) && $receiptSend) { $formValues['contact_id'] = $this->_contactID; $formValues['contribution_id'] = $contributionId; // send email receipt $mailSend = self::emailReceipt($this, $formValues, $membership); } if ($this->_action & CRM_Core_Action::UPDATE) { //end date can be modified by hooks, so if end date is set then use it. $endDate = $membership->end_date ? $membership->end_date : $endDate; $statusMsg = ts('Membership for %1 has been updated.', array(1 => $this->_memberDisplayName)); if ($endDate && $endDate !== 'null') { $endDate = CRM_Utils_Date::customFormat($endDate); $statusMsg .= ' ' . ts('The membership End Date is %1.', array(1 => $endDate)); } if ($receiptSend) { $statusMsg .= ' ' . ts('A confirmation and receipt has been sent to %1.', array(1 => $this->_contributorEmail)); } } elseif ($this->_action & CRM_Core_Action::ADD) { // FIX ME: fix status messages $statusMsg = array(); foreach ($membershipTypes as $memType => $membershipType) { $statusMsg[$memType] = ts('%1 membership for %2 has been added.', array(1 => $membershipType, 2 => $this->_memberDisplayName)); $membership = $createdMemberships[$memType]; $memEndDate = $membership->end_date ? $membership->end_date : $endDate; //get the end date from calculated dates. if (!$memEndDate && empty($params['is_recur'])) { $memEndDate = CRM_Utils_Array::value('end_date', $calcDates[$memType]); } if ($memEndDate && $memEndDate !== 'null') { $memEndDate = CRM_Utils_Date::customFormat($memEndDate); $statusMsg[$memType] .= ' ' . ts('The new membership End Date is %1.', array(1 => $memEndDate)); } } $statusMsg = implode('<br/>', $statusMsg); if ($receiptSend && !empty($mailSend)) { $statusMsg .= ' ' . ts('A membership confirmation and receipt has been sent to %1.', array(1 => $this->_contributorEmail)); } } // finally set membership id if already not set if (!$this->_id) { $this->_id = $membership->id; } CRM_Core_Session::setStatus($statusMsg, ts('Complete'), 'success'); $buttonName = $this->controller->getButtonName(); if ($this->_context == 'standalone') { if ($buttonName == $this->getButtonName('upload', 'new')) { $session->replaceUserContext(CRM_Utils_System::url('civicrm/member/add', 'reset=1&action=add&context=standalone')); } else { $session->replaceUserContext(CRM_Utils_System::url('civicrm/contact/view', "reset=1&cid={$this->_contactID}&selectedChild=member")); } } elseif ($buttonName == $this->getButtonName('upload', 'new')) { $session->replaceUserContext(CRM_Utils_System::url('civicrm/contact/view/membership', "reset=1&action=add&context=membership&cid={$this->_contactID}")); } }
/** * Submit function. * * This is also accessed by unit tests. * * @param array $formValues * * @return array */ public function submit($formValues) { $isTest = $this->_mode == 'test' ? 1 : 0; $joinDate = $startDate = $endDate = NULL; $membershipTypes = $membership = $calcDate = array(); $membershipType = NULL; $mailSend = FALSE; $formValues = $this->setPriceSetParameters($formValues); $params = $softParams = $ids = array(); $allMemberStatus = CRM_Member_PseudoConstant::membershipStatus(); $allContributionStatus = CRM_Contribute_PseudoConstant::contributionStatus(); if ($this->_id) { $ids['membership'] = $params['id'] = $this->_id; } $ids['userId'] = CRM_Core_Session::singleton()->get('userID'); // Set variables that we normally get from context. // In form mode these are set in preProcess. //TODO: set memberships, fixme $this->setContextVariables($formValues); $this->_memTypeSelected = self::getSelectedMemberships($this->_priceSet, $formValues); if (empty($formValues['financial_type_id'])) { $formValues['financial_type_id'] = $this->_priceSet['financial_type_id']; } $config = CRM_Core_Config::singleton(); $this->convertDateFieldsToMySQL($formValues); $membershipTypeValues = array(); foreach ($this->_memTypeSelected as $memType) { $membershipTypeValues[$memType]['membership_type_id'] = $memType; } //take the required membership recur values. if ($this->_mode && !empty($formValues['auto_renew'])) { $params['is_recur'] = $formValues['is_recur'] = TRUE; $mapping = array('frequency_interval' => 'duration_interval', 'frequency_unit' => 'duration_unit'); $count = 0; foreach ($this->_memTypeSelected as $memType) { $recurMembershipTypeValues = CRM_Utils_Array::value($memType, $this->_recurMembershipTypes, array()); foreach ($mapping as $mapVal => $mapParam) { $membershipTypeValues[$memType][$mapVal] = CRM_Utils_Array::value($mapParam, $recurMembershipTypeValues); if (!$count) { $formValues[$mapVal] = CRM_Utils_Array::value($mapParam, $recurMembershipTypeValues); } } $count++; } } $isQuickConfig = $this->_priceSet['is_quick_config']; $termsByType = array(); $lineItem = array($this->_priceSetId => array()); CRM_Price_BAO_PriceSet::processAmount($this->_priceSet['fields'], $formValues, $lineItem[$this->_priceSetId]); if (CRM_Utils_Array::value('tax_amount', $formValues)) { $params['tax_amount'] = $formValues['tax_amount']; } $params['total_amount'] = CRM_Utils_Array::value('amount', $formValues); $submittedFinancialType = CRM_Utils_Array::value('financial_type_id', $formValues); if (!empty($lineItem[$this->_priceSetId])) { foreach ($lineItem[$this->_priceSetId] as &$li) { if (!empty($li['membership_type_id'])) { if (!empty($li['membership_num_terms'])) { $termsByType[$li['membership_type_id']] = $li['membership_num_terms']; } } ///CRM-11529 for quick config backoffice transactions //when financial_type_id is passed in form, update the //lineitems with the financial type selected in form if ($isQuickConfig && $submittedFinancialType) { $li['financial_type_id'] = $submittedFinancialType; } } } $this->storeContactFields($formValues); $params['contact_id'] = $this->_contactID; $fields = array('status_id', 'source', 'is_override', 'campaign_id'); foreach ($fields as $f) { $params[$f] = CRM_Utils_Array::value($f, $formValues); } // fix for CRM-3724 // when is_override false ignore is_admin statuses during membership // status calculation. similarly we did fix for import in CRM-3570. if (empty($params['is_override'])) { $params['exclude_is_admin'] = TRUE; } // process date params to mysql date format. $dateTypes = array('join_date' => 'joinDate', 'start_date' => 'startDate', 'end_date' => 'endDate'); foreach ($dateTypes as $dateField => $dateVariable) { ${$dateVariable} = CRM_Utils_Date::processDate($formValues[$dateField]); } $memTypeNumTerms = empty($termsByType) ? CRM_Utils_Array::value('num_terms', $formValues) : NULL; $calcDates = array(); foreach ($this->_memTypeSelected as $memType) { if (empty($memTypeNumTerms)) { $memTypeNumTerms = CRM_Utils_Array::value($memType, $termsByType, 1); } $calcDates[$memType] = CRM_Member_BAO_MembershipType::getDatesForMembershipType($memType, $joinDate, $startDate, $endDate, $memTypeNumTerms); } foreach ($calcDates as $memType => $calcDate) { foreach (array_keys($dateTypes) as $d) { //first give priority to form values then calDates. $date = CRM_Utils_Array::value($d, $formValues); if (!$date) { $date = CRM_Utils_Array::value($d, $calcDate); } $membershipTypeValues[$memType][$d] = CRM_Utils_Date::processDate($date); } } // max related memberships - take from form or inherit from membership type foreach ($this->_memTypeSelected as $memType) { if (array_key_exists('max_related', $formValues)) { $membershipTypeValues[$memType]['max_related'] = CRM_Utils_Array::value('max_related', $formValues); } $membershipTypeValues[$memType]['custom'] = CRM_Core_BAO_CustomField::postProcess($formValues, $this->_id, 'Membership'); $membershipTypes[$memType] = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipType', $memType); } $membershipType = implode(', ', $membershipTypes); // Retrieve the name and email of the current user - this will be the FROM for the receipt email list($userName) = CRM_Contact_BAO_Contact_Location::getEmailDetails($ids['userId']); //CRM-13981, allow different person as a soft-contributor of chosen type if ($this->_contributorContactID != $this->_contactID) { $params['contribution_contact_id'] = $this->_contributorContactID; if (!empty($formValues['soft_credit_type_id'])) { $softParams['soft_credit_type_id'] = $formValues['soft_credit_type_id']; $softParams['contact_id'] = $this->_contactID; } } if (!empty($formValues['record_contribution'])) { $recordContribution = array('total_amount', 'financial_type_id', 'payment_instrument_id', 'trxn_id', 'contribution_status_id', 'check_number', 'campaign_id', 'receive_date'); foreach ($recordContribution as $f) { $params[$f] = CRM_Utils_Array::value($f, $formValues); } if (!$this->_onlinePendingContributionId) { if (empty($formValues['source'])) { $params['contribution_source'] = ts('%1 Membership: Offline signup (by %2)', array(1 => $membershipType, 2 => $userName)); } else { $params['contribution_source'] = $formValues['source']; } } if (empty($params['is_override']) && CRM_Utils_Array::value('contribution_status_id', $params) == array_search('Pending', CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name'))) { $params['status_id'] = array_search('Pending', $allMemberStatus); $params['skipStatusCal'] = TRUE; $params['is_pay_later'] = 1; $this->assign('is_pay_later', 1); } if (!empty($formValues['send_receipt'])) { $params['receipt_date'] = CRM_Utils_Array::value('receive_date', $formValues); } //insert financial type name in receipt. $formValues['contributionType_name'] = CRM_Core_DAO::getFieldValue('CRM_Financial_DAO_FinancialType', $formValues['financial_type_id']); } // process line items, until no previous line items. if (!empty($lineItem)) { $params['lineItems'] = $lineItem; $params['processPriceSet'] = TRUE; } $createdMemberships = array(); if ($this->_mode) { $params['total_amount'] = CRM_Utils_Array::value('total_amount', $formValues, 0); if (!$isQuickConfig) { $params['financial_type_id'] = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceSet', $this->_priceSetId, 'financial_type_id'); } else { $params['financial_type_id'] = CRM_Utils_Array::value('financial_type_id', $formValues); } $this->_paymentProcessor = CRM_Financial_BAO_PaymentProcessor::getPayment($formValues['payment_processor_id'], $this->_mode); //get the payment processor id as per mode. $params['payment_processor_id'] = $formValues['payment_processor_id'] = $this->_paymentProcessor['id']; $now = date('YmdHis'); $fields = array(); // set email for primary location. $fields['email-Primary'] = 1; $formValues['email-5'] = $formValues['email-Primary'] = $this->_memberEmail; $params['register_date'] = $now; // now set the values for the billing location. foreach ($this->_fields as $name => $dontCare) { $fields[$name] = 1; } // also add location name to the array $formValues["address_name-{$this->_bltID}"] = CRM_Utils_Array::value('billing_first_name', $formValues) . ' ' . CRM_Utils_Array::value('billing_middle_name', $formValues) . ' ' . CRM_Utils_Array::value('billing_last_name', $formValues); $formValues["address_name-{$this->_bltID}"] = trim($formValues["address_name-{$this->_bltID}"]); $fields["address_name-{$this->_bltID}"] = 1; //ensure we don't over-write the payer's email with the member's email if ($this->_contributorContactID == $this->_contactID) { $fields["email-{$this->_bltID}"] = 1; } $nameFields = array('first_name', 'middle_name', 'last_name'); foreach ($nameFields as $name) { $fields[$name] = 1; if (array_key_exists("billing_{$name}", $formValues)) { $formValues[$name] = $formValues["billing_{$name}"]; $formValues['preserveDBName'] = TRUE; } } if ($this->_contributorContactID == $this->_contactID) { //see CRM-12869 for discussion of why we don't do this for separate payee payments CRM_Contact_BAO_Contact::createProfileContact($formValues, $fields, $this->_contributorContactID, NULL, NULL, CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $this->_contactID, 'contact_type')); } // add all the additional payment params we need $formValues["state_province-{$this->_bltID}"] = $formValues["billing_state_province-{$this->_bltID}"] = CRM_Core_PseudoConstant::stateProvinceAbbreviation($formValues["billing_state_province_id-{$this->_bltID}"]); $formValues["country-{$this->_bltID}"] = $formValues["billing_country-{$this->_bltID}"] = CRM_Core_PseudoConstant::countryIsoCode($formValues["billing_country_id-{$this->_bltID}"]); $formValues['year'] = CRM_Core_Payment_Form::getCreditCardExpirationYear($formValues); $formValues['month'] = CRM_Core_Payment_Form::getCreditCardExpirationMonth($formValues); $formValues['ip_address'] = CRM_Utils_System::ipAddress(); $formValues['amount'] = $params['total_amount']; $formValues['currencyID'] = $config->defaultCurrency; $formValues['description'] = ts("Contribution submitted by a staff person using member's credit card for signup"); $formValues['invoiceID'] = md5(uniqid(rand(), TRUE)); $formValues['financial_type_id'] = $params['financial_type_id']; // 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 = $formValues; $paymentParams['contactID'] = $this->_contributorContactID; //CRM-10377 if payment is by an alternate contact then we need to set that person // as the contact in the payment params if ($this->_contributorContactID != $this->_contactID) { if (!empty($formValues['soft_credit_type_id'])) { $softParams['contact_id'] = $params['contact_id']; $softParams['soft_credit_type_id'] = $formValues['soft_credit_type_id']; } } if (!empty($formValues['send_receipt'])) { $paymentParams['email'] = $this->_contributorEmail; } CRM_Core_Payment_Form::mapParams($this->_bltID, $formValues, $paymentParams, TRUE); // CRM-7137 -for recurring membership, // we do need contribution and recurring records. $result = NULL; if (!empty($paymentParams['is_recur'])) { $financialType = new CRM_Financial_DAO_FinancialType(); $financialType->id = $params['financial_type_id']; $financialType->find(TRUE); $this->_params = $formValues; $contribution = CRM_Contribute_Form_Contribution_Confirm::processFormContribution($this, $paymentParams, NULL, array('contact_id' => $this->_contributorContactID, 'line_item' => $lineItem, 'is_test' => $isTest, 'campaign_id' => CRM_Utils_Array::value('campaign_id', $paymentParams), 'contribution_page_id' => CRM_Utils_Array::value('contribution_page_id', $formValues), 'source' => CRM_Utils_Array::value('source', $paymentParams, CRM_Utils_Array::value('description', $paymentParams)), 'thankyou_date' => CRM_Utils_Array::value('thankyou_date', $paymentParams), 'payment_instrument_id' => $this->_paymentProcessor['payment_instrument_id']), $financialType, TRUE, FALSE, $this->_bltID); //create new soft-credit record, CRM-13981 if ($softParams) { $softParams['contribution_id'] = $contribution->id; $softParams['currency'] = $contribution->currency; $softParams['amount'] = $contribution->total_amount; CRM_Contribute_BAO_ContributionSoft::add($softParams); } $paymentParams['contactID'] = $this->_contactID; $paymentParams['contributionID'] = $contribution->id; $paymentParams['contributionTypeID'] = $contribution->financial_type_id; $paymentParams['contributionPageID'] = $contribution->contribution_page_id; $paymentParams['contributionRecurID'] = $contribution->contribution_recur_id; $ids['contribution'] = $contribution->id; $params['contribution_recur_id'] = $paymentParams['contributionRecurID']; } if ($params['total_amount'] > 0.0) { $payment = $this->_paymentProcessor['object']; try { $result = $payment->doPayment($paymentParams); $formValues = array_merge($formValues, $result); // Assign amount to template if payment was successful. $this->assign('amount', $params['total_amount']); } catch (PaymentProcessorException $e) { if (!empty($paymentParams['contributionID'])) { CRM_Contribute_BAO_Contribution::failPayment($paymentParams['contributionID'], $this->_contactID, $e->getMessage()); } if (!empty($paymentParams['contributionRecurID'])) { CRM_Contribute_BAO_ContributionRecur::deleteRecurContribution($paymentParams['contributionRecurID']); } CRM_Core_Error::displaySessionError($result); CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/contact/view/membership', "reset=1&action=add&cid={$this->_contactID}&context=&mode={$this->_mode}")); } } if ($formValues['payment_status_id'] != array_search('Completed', $allContributionStatus)) { $params['status_id'] = array_search('Pending', $allMemberStatus); $params['skipStatusCal'] = TRUE; // unset send-receipt option, since receipt will be sent when ipn is received. unset($formValues['send_receipt'], $formValues['send_receipt']); //as membership is pending set dates to null. $memberDates = array('join_date' => 'joinDate', 'start_date' => 'startDate', 'end_date' => 'endDate'); foreach ($memberDates as $dv) { ${$dv} = NULL; foreach ($this->_memTypeSelected as $memType) { $membershipTypeValues[$memType][$dv] = NULL; } } } $params['receive_date'] = $now; $params['invoice_id'] = $formValues['invoiceID']; $params['contribution_source'] = ts('%1 Membership Signup: Credit card or direct debit (by %2)', array(1 => $membershipType, 2 => $userName)); $params['source'] = $formValues['source'] ? $formValues['source'] : $params['contribution_source']; $params['trxn_id'] = CRM_Utils_Array::value('trxn_id', $result); $params['payment_instrument_id'] = 1; $params['is_test'] = $this->_mode == 'live' ? 0 : 1; if (!empty($formValues['send_receipt'])) { $params['receipt_date'] = $now; } else { $params['receipt_date'] = NULL; } $this->set('params', $formValues); $this->assign('trxn_id', CRM_Utils_Array::value('trxn_id', $result)); $this->assign('receive_date', CRM_Utils_Date::mysqlToIso($params['receive_date'])); // required for creating membership for related contacts $params['action'] = $this->_action; //create membership record. $count = 0; foreach ($this->_memTypeSelected as $memType) { if ($count && ($relateContribution = CRM_Member_BAO_Membership::getMembershipContributionId($membership->id))) { $membershipTypeValues[$memType]['relate_contribution_id'] = $relateContribution; } $membershipParams = array_merge($membershipTypeValues[$memType], $params); //CRM-15366 if (!empty($softParams) && empty($paymentParams['is_recur'])) { $membershipParams['soft_credit'] = $softParams; } // This is required to trigger the recording of the membership contribution in the // CRM_Member_BAO_Membership::Create function. // @todo stop setting this & 'teach' the create function to respond to something // appropriate as part of our 2-step always create the pending contribution & then finally add the payment // process - // @see http://wiki.civicrm.org/confluence/pages/viewpage.action?pageId=261062657#Payments&AccountsRoadmap-Movetowardsalwaysusinga2-steppaymentprocess $membershipParams['contribution_status_id'] = CRM_Utils_Array::value('payment_status_id', $result); if (!empty($paymentParams['is_recur'])) { // The earlier process created the line items (although we want to get rid of the earlier one in favour // of a single path! unset($membershipParams['lineItems']); } $membership = CRM_Member_BAO_Membership::create($membershipParams, $ids); $params['contribution'] = CRM_Utils_Array::value('contribution', $membershipParams); unset($params['lineItems']); $this->_membershipIDs[] = $membership->id; $createdMemberships[$memType] = $membership; $count++; } } else { $params['action'] = $this->_action; if ($this->_onlinePendingContributionId && !empty($formValues['record_contribution'])) { // update membership as well as contribution object, CRM-4395 $params['contribution_id'] = $this->_onlinePendingContributionId; $params['componentId'] = $params['id']; $params['componentName'] = 'contribute'; $result = CRM_Contribute_BAO_Contribution::transitionComponents($params, TRUE); if (!empty($result) && !empty($params['contribution_id'])) { $lineItem = array(); $lineItems = CRM_Price_BAO_LineItem::getLineItems($params['contribution_id'], 'contribution', NULL, TRUE, TRUE); $itemId = key($lineItems); $priceSetId = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceField', $lineItems[$itemId]['price_field_id'], 'price_set_id'); $lineItems[$itemId]['unit_price'] = $params['total_amount']; $lineItems[$itemId]['line_total'] = $params['total_amount']; $lineItems[$itemId]['id'] = $itemId; $lineItem[$priceSetId] = $lineItems; $contributionBAO = new CRM_Contribute_BAO_Contribution(); $contributionBAO->id = $params['contribution_id']; $contributionBAO->contact_id = $params['contact_id']; $contributionBAO->find(); CRM_Price_BAO_LineItem::processPriceSet($params['contribution_id'], $lineItem, $contributionBAO, 'civicrm_membership'); //create new soft-credit record, CRM-13981 if ($softParams) { $softParams['contribution_id'] = $params['contribution_id']; while ($contributionBAO->fetch()) { $softParams['currency'] = $contributionBAO->currency; $softParams['amount'] = $contributionBAO->total_amount; } CRM_Contribute_BAO_ContributionSoft::add($softParams); } } //carry updated membership object. $membership = new CRM_Member_DAO_Membership(); $membership->id = $this->_id; $membership->find(TRUE); $cancelled = TRUE; if ($membership->end_date) { //display end date w/ status message. $endDate = $membership->end_date; if (!in_array($membership->status_id, array(array_search('Cancelled', CRM_Member_PseudoConstant::membershipStatus(NULL, " name = 'Cancelled' ", 'name', FALSE, TRUE)), array_search('Expired', CRM_Member_PseudoConstant::membershipStatus())))) { $cancelled = FALSE; } } // suppress form values in template. $this->assign('cancelled', $cancelled); $createdMemberships[] = $membership; } else { $count = 0; foreach ($this->_memTypeSelected as $memType) { if ($count && !empty($formValues['record_contribution']) && ($relateContribution = CRM_Member_BAO_Membership::getMembershipContributionId($membership->id))) { $membershipTypeValues[$memType]['relate_contribution_id'] = $relateContribution; } $membershipParams = array_merge($params, $membershipTypeValues[$memType]); if (!empty($formValues['int_amount'])) { $init_amount = array(); foreach ($formValues as $key => $value) { if (strstr($key, 'txt-price')) { $init_amount[$key] = $value; } } $membershipParams['init_amount'] = $init_amount; } if (!empty($softParams)) { $membershipParams['soft_credit'] = $softParams; } $membership = CRM_Member_BAO_Membership::create($membershipParams, $ids); $params['contribution'] = CRM_Utils_Array::value('contribution', $membershipParams); unset($params['lineItems']); $this->_membershipIDs[] = $membership->id; $createdMemberships[$memType] = $membership; $count++; } } } if (!empty($lineItem[$this->_priceSetId])) { $invoiceSettings = CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::CONTRIBUTE_PREFERENCES_NAME, 'contribution_invoice_settings'); $invoicing = CRM_Utils_Array::value('invoicing', $invoiceSettings); $taxAmount = FALSE; $totalTaxAmount = 0; foreach ($lineItem[$this->_priceSetId] as &$priceFieldOp) { if (!empty($priceFieldOp['membership_type_id'])) { $priceFieldOp['start_date'] = $membershipTypeValues[$priceFieldOp['membership_type_id']]['start_date'] ? CRM_Utils_Date::customFormat($membershipTypeValues[$priceFieldOp['membership_type_id']]['start_date'], '%B %E%f, %Y') : '-'; $priceFieldOp['end_date'] = $membershipTypeValues[$priceFieldOp['membership_type_id']]['end_date'] ? CRM_Utils_Date::customFormat($membershipTypeValues[$priceFieldOp['membership_type_id']]['end_date'], '%B %E%f, %Y') : '-'; } else { $priceFieldOp['start_date'] = $priceFieldOp['end_date'] = 'N/A'; } if ($invoicing && isset($priceFieldOp['tax_amount'])) { $taxAmount = TRUE; $totalTaxAmount += $priceFieldOp['tax_amount']; } } if ($invoicing) { $dataArray = array(); foreach ($lineItem[$this->_priceSetId] as $key => $value) { if (isset($value['tax_amount']) && isset($value['tax_rate'])) { if (isset($dataArray[$value['tax_rate']])) { $dataArray[$value['tax_rate']] = $dataArray[$value['tax_rate']] + CRM_Utils_Array::value('tax_amount', $value); } else { $dataArray[$value['tax_rate']] = CRM_Utils_Array::value('tax_amount', $value); } } } if ($taxAmount) { $this->assign('totalTaxAmount', $totalTaxAmount); $this->assign('taxTerm', CRM_Utils_Array::value('tax_term', $invoiceSettings)); } $this->assign('dataArray', $dataArray); } } $this->assign('lineItem', !empty($lineItem) && !$isQuickConfig ? $lineItem : FALSE); $receiptSend = FALSE; $contributionId = CRM_Member_BAO_Membership::getMembershipContributionId($membership->id); $membershipIds = $this->_membershipIDs; if ($contributionId && !empty($membershipIds)) { $contributionDetails = CRM_Contribute_BAO_Contribution::getContributionDetails(CRM_Export_Form_Select::MEMBER_EXPORT, $this->_membershipIDs); if ($contributionDetails[$membership->id]['contribution_status'] == 'Completed') { $receiptSend = TRUE; } } if (!empty($formValues['send_receipt']) && $receiptSend) { $formValues['contact_id'] = $this->_contactID; $formValues['contribution_id'] = $contributionId; // We really don't need a distinct receipt_text_signup vs receipt_text_renewal as they are // handled in the receipt. But by setting one we avoid breaking templates for now // although at some point we should switch in the templates. $formValues['receipt_text_signup'] = $formValues['receipt_text']; // send email receipt $mailSend = self::emailReceipt($this, $formValues, $membership); } // finally set membership id if already not set if (!$this->_id) { $this->_id = $membership->id; } $isRecur = CRM_Utils_Array::value('is_recur', $params); $this->setStatusMessage($membership, $endDate, $receiptSend, $membershipTypes, $createdMemberships, $isRecur, $calcDates, $mailSend); return $createdMemberships; }
/** * Given the list of params in the params array, fetch the object * and store the values in the values array * * @param array $params input parameters to find object * @param array $values output values of the object * @param array $ids the array that holds all the db ids * * @return CRM_Contribute_BAO_Contribution|null the found object or null * @access public * @static */ static function &getValues(&$params, &$values, &$ids) { if (empty($params)) { return null; } $contribution = new CRM_Contribute_BAO_Contribution(); $contribution->copyValues($params); if ($contribution->find(true)) { $ids['contribution'] = $contribution->id; CRM_Core_DAO::storeValues($contribution, $values); return $contribution; } return null; }
/** * Complete an existing (pending) transaction. * * This will update related entities (participant, membership, pledge etc) * and take any complete actions from the contribution page (e.g. send receipt). * * @todo - most of this should live in the BAO layer but as we want it to be an addition * to 4.3 which is already stable we should add it to the api layer & re-factor into the BAO layer later * * @param array $params * Input parameters. * * @throws API_Exception * Api result array. */ function civicrm_api3_contribution_repeattransaction(&$params) { $input = $ids = array(); $contribution = new CRM_Contribute_BAO_Contribution(); $contribution->id = $params['original_contribution_id']; if (!$contribution->find(TRUE)) { throw new API_Exception('A valid original contribution ID is required', 'invalid_data'); } $original_contribution = clone $contribution; try { if (!$contribution->loadRelatedObjects($input, $ids, TRUE)) { throw new API_Exception('failed to load related objects'); } unset($contribution->id, $contribution->receive_date, $contribution->invoice_id); $contribution->contribution_status_id = $params['contribution_status_id']; $contribution->receive_date = $params['receive_date']; $passThroughParams = array('trxn_id', 'total_amount', 'campaign_id', 'fee_amount'); $input = array_intersect_key($params, array_fill_keys($passThroughParams, NULL)); $params = _ipn_process_transaction($params, $contribution, $input, $ids, $original_contribution); } catch (Exception $e) { throw new API_Exception('failed to load related objects' . $e->getMessage() . "\n" . $e->getTraceAsString()); } }
/** * Process recurring contributions * @param array $input * @param array $ids * @param array $objects * @param boolean $first * @return void|boolean */ function recur(&$input, &$ids, &$objects, $first) { if (!isset($input['txnType'])) { CRM_Core_Error::debug_log_message("Could not find txn_type in input request"); echo "Failure: Invalid parameters<p>"; return FALSE; } if ($input['txnType'] == 'recurring_payment' && $input['paymentStatus'] != 'Completed') { CRM_Core_Error::debug_log_message("Ignore all IPN payments that are not completed"); echo "Failure: Invalid parameters<p>"; return FALSE; } $recur =& $objects['contributionRecur']; // make sure the invoice ids match // make sure the invoice is valid and matches what we have in // the contribution record if ($recur->invoice_id != $input['invoice']) { CRM_Core_Error::debug_log_message("Invoice values dont match between database and IPN request recur is " . $recur->invoice_id . " input is " . $input['invoice']); echo "Failure: Invoice values dont match between database and IPN request recur is " . $recur->invoice_id . " input is " . $input['invoice']; return FALSE; } $now = date('YmdHis'); // fix dates that already exist $dates = array('create', 'start', 'end', 'cancel', 'modified'); foreach ($dates as $date) { $name = "{$date}_date"; if ($recur->{$name}) { $recur->{$name} = CRM_Utils_Date::isoToMysql($recur->{$name}); } } $sendNotification = FALSE; $subscriptionPaymentStatus = NULL; //List of Transaction Type /* recurring_payment_profile_created RP Profile Created recurring_payment RP Sucessful Payment recurring_payment_failed RP Failed Payment recurring_payment_profile_cancel RP Profile Cancelled recurring_payment_expired RP Profile Expired recurring_payment_skipped RP Profile Skipped recurring_payment_outstanding_payment RP Sucessful Outstanding Payment recurring_payment_outstanding_payment_failed RP Failed Outstanding Payment recurring_payment_suspended RP Profile Suspended recurring_payment_suspended_due_to_max_failed_payment RP Profile Suspended due to Max Failed Payment */ //set transaction type $txnType = $this->retrieve('txn_type', 'String'); //Changes for paypal pro recurring payment $contributionStatuses = civicrm_api3('contribution', 'getoptions', array('field' => 'contribution_status_id')); $contributionStatuses = $contributionStatuses['values']; switch ($txnType) { case 'recurring_payment_profile_created': if (in_array($recur->contribution_status_id, array(array_search('Pending', $contributionStatuses), array_search('In Progress', $contributionStatuses))) && !empty($recur->processor_id)) { echo "already handled"; return; } $recur->create_date = $now; $recur->contribution_status_id = 2; $recur->processor_id = $this->retrieve('recurring_payment_id', 'String'); $recur->trxn_id = $recur->processor_id; $subscriptionPaymentStatus = CRM_Core_Payment::RECURRING_PAYMENT_START; $sendNotification = TRUE; break; case 'recurring_payment': if ($first) { $recur->start_date = $now; } else { $recur->modified_date = $now; } //contribution installment is completed if ($this->retrieve('profile_status', 'String') == 'Expired') { if (!empty($recur->end_date)) { echo "already handled"; return; } $recur->contribution_status_id = 1; $recur->end_date = $now; $sendNotification = TRUE; $subscriptionPaymentStatus = CRM_Core_Payment::RECURRING_PAYMENT_END; } // make sure the contribution status is not done // since order of ipn's is unknown if ($recur->contribution_status_id != 1) { $recur->contribution_status_id = 5; } break; } $recur->save(); if ($sendNotification) { $autoRenewMembership = FALSE; if ($recur->id && isset($ids['membership']) && $ids['membership']) { $autoRenewMembership = TRUE; } //send recurring Notification email for user CRM_Contribute_BAO_ContributionPage::recurringNotify($subscriptionPaymentStatus, $ids['contact'], $ids['contributionPage'], $recur, $autoRenewMembership); } if ($txnType != 'recurring_payment') { return; } if (!$first) { //check if this contribution transaction is already processed //if not create a contribution and then get it processed $contribution = new CRM_Contribute_BAO_Contribution(); $contribution->trxn_id = $input['trxn_id']; if ($contribution->trxn_id && $contribution->find()) { CRM_Core_Error::debug_log_message("returning since contribution has already been handled"); echo "Success: Contribution has already been handled<p>"; return TRUE; } $contribution->contact_id = $recur->contact_id; $contribution->financial_type_id = $objects['contributionType']->id; $contribution->contribution_page_id = $ids['contributionPage']; $contribution->contribution_recur_id = $ids['contributionRecur']; $contribution->currency = $objects['contribution']->currency; $contribution->payment_instrument_id = $objects['contribution']->payment_instrument_id; $contribution->amount_level = $objects['contribution']->amount_level; $contribution->campaign_id = $objects['contribution']->campaign_id; $objects['contribution'] =& $contribution; } // CRM-13737 - am not aware of any reason why payment_date would not be set - this if is a belt & braces $objects['contribution']->receive_date = !empty($input['payment_date']) ? date('YmdHis', strtotime($input['payment_date'])) : $now; $this->single($input, $ids, $objects, TRUE, $first); }
/** * This function ensures that we have the right input contribution parameters * * We also need to make sure we run all the form rules on the params list * to ensure that the params are valid * * @param array $params Associative array of property name/value * pairs to insert in new contribution. * * @return bool|CRM_Utils_Error * @access private */ function _civicrm_contribute_check_params(&$params) { static $required = array('contact_id' => NULL, 'total_amount' => NULL, 'contribution_type_id' => 'contribution_type'); // params should be an array if (!is_array($params)) { return civicrm_create_error(ts('Input parameters is not an array')); } // cannot create a contribution with empty params if (empty($params)) { return civicrm_create_error('Input Parameters empty'); } $valid = TRUE; $error = ''; // check params for contribution id during update if (CRM_Utils_Array::value('id', $params)) { require_once 'CRM/Contribute/BAO/Contribution.php'; $contributor = new CRM_Contribute_BAO_Contribution(); $contributor->id = $params['id']; if (!$contributor->find(TRUE)) { return civicrm_create_error(ts('Contribution id is not valid')); } // do not check other field during update return array(); } foreach ($required as $field => $eitherField) { if (!CRM_Utils_Array::value($field, $params)) { if ($eitherField && CRM_Utils_Array::value($eitherField, $params)) { continue; } $valid = FALSE; $error .= $field; break; } } if (!$valid) { return civicrm_create_error("Required fields not found for contribution {$error}"); } return array(); }