/** * Takes an associative array and creates a contribution object. * * the function extract all the params it needs to initialize the create a * contribution object. the params array could contain additional unused name/value * pairs * * @param array $params * (reference ) an assoc array of name/value pairs. * * @return CRM_Contribute_BAO_Contribution * @todo move hook calls / extended logic to create - requires changing calls to call create not add */ public static function add(&$params) { if (!empty($params['id'])) { CRM_Utils_Hook::pre('edit', 'ContributionRecur', $params['id'], $params); } else { CRM_Utils_Hook::pre('create', 'ContributionRecur', NULL, $params); } // make sure we're not creating a new recurring contribution with the same transaction ID // or invoice ID as an existing recurring contribution $duplicates = array(); if (self::checkDuplicate($params, $duplicates)) { $error = CRM_Core_Error::singleton(); $d = implode(', ', $duplicates); $error->push(CRM_Core_Error::DUPLICATE_CONTRIBUTION, 'Fatal', array($d), "Found matching recurring contribution(s): {$d}"); return $error; } $recurring = new CRM_Contribute_BAO_ContributionRecur(); $recurring->copyValues($params); $recurring->id = CRM_Utils_Array::value('id', $params); // set currency for CRM-1496 if (empty($params['id']) && !isset($recurring->currency)) { $config = CRM_Core_Config::singleton(); $recurring->currency = $config->defaultCurrency; } $result = $recurring->save(); if (!empty($params['id'])) { CRM_Utils_Hook::post('edit', 'ContributionRecur', $recurring->id, $recurring); } else { CRM_Utils_Hook::post('create', 'ContributionRecur', $recurring->id, $recurring); } if (!empty($params['custom']) && is_array($params['custom'])) { CRM_Core_BAO_CustomValueTable::store($params['custom'], 'civicrm_contribution_recur', $recurring->id); } return $result; }
/** * Test that an object can be retrieved & saved (per CRM-14986). * * This has been causing a DB error so we are checking for absence of error */ public function testFindSave() { $contributionRecur = $this->callAPISuccess('contribution_recur', 'create', $this->_params); $dao = new CRM_Contribute_BAO_ContributionRecur(); $dao->id = $contributionRecur['id']; $dao->find(TRUE); $dao->is_email_receipt = 0; $dao->save(); }
/** * takes an associative array and creates a contribution object * * the function extract all the params it needs to initialize the create a * contribution object. the params array could contain additional unused name/value * pairs * * @param array $params (reference ) an assoc array of name/value pairs * @param array $ids the array that holds all the db ids * * @return object CRM_Contribute_BAO_Contribution object * @access public * @static */ static function add(&$params, &$ids) { $duplicates = array(); if (self::checkDuplicate($params, $duplicates)) { $error =& CRM_Core_Error::singleton(); $d = implode(', ', $duplicates); $error->push(CRM_Core_Error::DUPLICATE_CONTRIBUTION, 'Fatal', array($d), "Found matching contribution(s): {$d}"); return $error; } $recurring = new CRM_Contribute_BAO_ContributionRecur(); $recurring->copyValues($params); $recurring->id = CRM_Utils_Array::value('contribution', $ids); // set currency for CRM-1496 if (!isset($recurring->currency)) { $config =& CRM_Core_Config::singleton(); $recurring->currency = $config->defaultCurrency; } return $recurring->save(); }
static function processAPIContribution($params) { if (empty($params) || array_key_exists('error', $params)) { return false; } // add contact using dedupe rule require_once 'CRM/Dedupe/Finder.php'; $dedupeParams = CRM_Dedupe_Finder::formatParams($params, 'Individual'); $dedupeParams['check_permission'] = false; $dupeIds = CRM_Dedupe_Finder::dupesByParams($dedupeParams, 'Individual'); // if we find more than one contact, use the first one if (CRM_Utils_Array::value(0, $dupeIds)) { $params['contact_id'] = $dupeIds[0]; } require_once 'CRM/Contact/BAO/Contact.php'; $contact = CRM_Contact_BAO_Contact::create($params); if (!$contact->id) { return false; } // only pass transaction params to contribution::create, if available if (array_key_exists('transaction', $params)) { $params = $params['transaction']; $params['contact_id'] = $contact->id; } // handle contribution custom data $customFields = CRM_Core_BAO_CustomField::getFields('Contribution', false, false, CRM_Utils_Array::value('contribution_type_id', $params)); $params['custom'] = CRM_Core_BAO_CustomField::postProcess($params, $customFields, CRM_Utils_Array::value('id', $params, null), 'Contribution'); // create contribution // if this is a recurring contribution then process it first if ($params['trxn_type'] == 'subscrpayment') { // see if a recurring record already exists require_once 'CRM/Contribute/BAO/ContributionRecur.php'; $recurring = new CRM_Contribute_BAO_ContributionRecur(); $recurring->processor_id = $params['processor_id']; if (!$recurring->find(true)) { $recurring = new CRM_Contribute_BAO_ContributionRecur(); $recurring->invoice_id = $params['invoice_id']; $recurring->find(true); } // This is the same thing the CiviCRM IPN handler does to handle // subsequent recurring payments to avoid duplicate contribution // errors due to invoice ID. See: // ./CRM/Core/Payment/PayPalIPN.php:200 if ($recurring->id) { $params['invoice_id'] = md5(uniqid(rand(), true)); } $recurring->copyValues($params); $recurring->save(); if (is_a($recurring, 'CRM_Core_Error')) { return false; } else { $params['contribution_recur_id'] = $recurring->id; } } require_once 'CRM/Contribute/BAO/Contribution.php'; $contribution =& CRM_Contribute_BAO_Contribution::create($params, CRM_Core_DAO::$_nullArray); if (!$contribution->id) { return false; } return true; }
/** * This is the counterpart to the doDirectPayment method. This method creates * partial mandates, where the subsequent payment processess produces a payment. * * This function here should be called after the payment process was completed. * It will process all the PARTIAL mandates and connect them with created contributions. */ public static function processPartialMandates() { // load all the PARTIAL mandates $partial_mandates = civicrm_api3('SepaMandate', 'get', array('version' => 3, 'status' => 'PARTIAL', 'option.limit' => 9999)); foreach ($partial_mandates['values'] as $mandate_id => $mandate) { if ($mandate['type'] == 'OOFF') { // in the OOFF case, we need to find the contribution, and connect it $contribution = civicrm_api('Contribution', 'getsingle', array('version' => 3, 'trxn_id' => $mandate['reference'])); if (empty($contribution['is_error'])) { // check collection date $ooff_notice = (int) CRM_Sepa_Logic_Settings::getSetting("batching.OOFF.notice", $mandate['creditor_id']); $first_collection_date = strtotime("+{$ooff_notice} days"); $collection_date = strtotime($contribution['receive_date']); if ($collection_date < $first_collection_date) { // adjust collection date to the earliest possible one $collection_date = $first_collection_date; } // FOUND! Update the contribution... $contribution_bao = new CRM_Contribute_BAO_Contribution(); $contribution_bao->get('id', $contribution['id']); $contribution_bao->is_pay_later = 0; $contribution_bao->receive_date = date('YmdHis', $collection_date); $contribution_bao->contribution_status_id = (int) CRM_Core_OptionGroup::getValue('contribution_status', 'Pending', 'name'); $contribution_bao->payment_instrument_id = (int) CRM_Core_OptionGroup::getValue('payment_instrument', 'OOFF', 'name'); $contribution_bao->save(); // ...and connect it to the mandate $mandate_update = array(); $mandate_update['id'] = $mandate['id']; $mandate_update['entity_id'] = $contribution['id']; $mandate_update['type'] = $mandate['type']; if (empty($mandate['contact_id'])) { // this happens when the payment gets created AFTER the doDirectPayment method $mandate_update['contact_id'] = $contribution_bao->contact_id; } // initialize according to the creditor settings CRM_Sepa_BAO_SEPACreditor::initialiseMandateData($mandate['creditor_id'], $mandate_update); // finally, write the changes to the mandate civicrm_api3('SepaMandate', 'create', $mandate_update); } else { // if NOT FOUND or error, delete the partial mandate civicrm_api3('SepaMandate', 'delete', array('id' => $mandate_id)); } } elseif ($mandate['type'] == 'RCUR') { // in the RCUR case, we also need to find the contribution, and connect it // load the contribution AND the associated recurring contribution $contribution = civicrm_api('Contribution', 'getsingle', array('version' => 3, 'trxn_id' => $mandate['reference'])); $rcontribution = civicrm_api('ContributionRecur', 'getsingle', array('version' => 3, 'trxn_id' => $mandate['reference'])); if (empty($contribution['is_error']) && empty($rcontribution['is_error'])) { // we need to set the receive date to the correct collection date, otherwise it will be created again (w/o) $rcur_notice = (int) CRM_Sepa_Logic_Settings::getSetting("batching.RCUR.notice", $mandate['creditor_id']); $now = strtotime(date('Y-m-d', strtotime("now +{$rcur_notice} days"))); // round to full day $collection_date = CRM_Sepa_Logic_Batching::getNextExecutionDate($rcontribution, $now); // fix contribution $contribution_bao = new CRM_Contribute_BAO_Contribution(); $contribution_bao->get('id', $contribution['id']); $contribution_bao->is_pay_later = 0; $contribution_bao->contribution_status_id = (int) CRM_Core_OptionGroup::getValue('contribution_status', 'Pending', 'name'); $contribution_bao->payment_instrument_id = (int) CRM_Core_OptionGroup::getValue('payment_instrument', 'FRST', 'name'); $contribution_bao->receive_date = date('YmdHis', strtotime($collection_date)); $contribution_bao->save(); // fix recurring contribution $rcontribution_bao = new CRM_Contribute_BAO_ContributionRecur(); $rcontribution_bao->get('id', $rcontribution['id']); $rcontribution_bao->start_date = date('YmdHis', strtotime($rcontribution_bao->start_date)); $rcontribution_bao->create_date = date('YmdHis', strtotime($rcontribution_bao->create_date)); $rcontribution_bao->modified_date = date('YmdHis', strtotime($rcontribution_bao->modified_date)); $rcontribution_bao->contribution_status_id = (int) CRM_Core_OptionGroup::getValue('contribution_status', 'Pending', 'name'); $rcontribution_bao->payment_instrument_id = (int) CRM_Core_OptionGroup::getValue('payment_instrument', 'FRST', 'name'); $rcontribution_bao->save(); // ...and connect it to the mandate $mandate_update = array(); $mandate_update['id'] = $mandate['id']; $mandate_update['entity_id'] = $rcontribution['id']; $mandate_update['type'] = $mandate['type']; if (empty($mandate['contact_id'])) { $mandate_update['contact_id'] = $contribution['contact_id']; $mandate['contact_id'] = $contribution['contact_id']; } //NO: $mandate_update['first_contribution_id'] = $contribution['id']; // initialize according to the creditor settings CRM_Sepa_BAO_SEPACreditor::initialiseMandateData($mandate['creditor_id'], $mandate_update); // finally, write the changes to the mandate civicrm_api3('SepaMandate', 'create', $mandate_update); // ...and trigger notification // FIXME: WORKAROUND, see https://github.com/Project60/org.project60.sepa/issues/296) CRM_Contribute_BAO_ContributionPage::recurringNotify(CRM_Core_Payment::RECURRING_PAYMENT_START, $mandate['contact_id'], $contribution_bao->contribution_page_id, $rcontribution_bao); } else { // something went wrong, delete partial error_log("org.project60.sepa: deleting partial mandate " . $mandate['reference']); civicrm_api3('SepaMandate', 'delete', array('id' => $mandate_id)); } } } }