/** * changes the amount of a SEPA mandate * * @return success as boolean * @author endres -at- systopia.de */ static function adjustAmount($mandate_id, $adjusted_amount) { $adjusted_amount = (double) $adjusted_amount; $contribution_id_pending = CRM_Core_OptionGroup::getValue('contribution_status', 'Pending', 'name'); // use a lock, in case somebody is batching just now $lock = CRM_Sepa_Logic_Settings::getLock(); if (empty($lock)) { CRM_Core_Session::setStatus(sprintf(ts("Cannot adjust mandate [%s], batching in progress!"), $mandate_id), ts('Error'), 'error'); return FALSE; } // first, load the mandate $mandate = civicrm_api("SepaMandate", "getsingle", array('id' => $mandate_id, 'version' => 3)); if (isset($mandate['is_error'])) { CRM_Core_Session::setStatus(sprintf(ts("Cannot read mandate [%s]. Error was: '%s'"), $mandate_id, $mandate['error_message']), ts('Error'), 'error'); $lock->release(); return FALSE; } // check the mandate type if ($mandate['type'] != "RCUR") { CRM_Core_Session::setStatus(ts("You can only adjust the amount of recurring contribution mandates."), ts('Error'), 'error'); $lock->release(); return FALSE; } // load the contribution $contribution_id = $mandate['entity_id']; $contribution = civicrm_api('ContributionRecur', "getsingle", array('id' => $contribution_id, 'version' => 3)); if (isset($contribution['is_error']) && $contribution['is_error']) { CRM_Core_Session::setStatus(sprintf(ts("Cannot read contribution [%s]. Error was: '%s'"), $contribution_id, $contribution['error_message']), ts('Error'), 'error'); $lock->release(); return FALSE; } // check the amount if ($adjusted_amount <= 0) { CRM_Core_Session::setStatus(ts("The amount cannot be changed to zero or less."), ts('Error'), 'error'); $lock->release(); return FALSE; } // check the amount $old_amount = (double) $contribution['amount']; if ($old_amount == $adjusted_amount) { CRM_Core_Session::setStatus(ts("The requested amount is the same as the current one."), ts('Error'), 'error'); $lock->release(); return FALSE; } // modify the amount in the recurring contribution $query = array('version' => 3, 'id' => $contribution_id, 'amount' => $adjusted_amount, 'currency' => 'EUR'); $result = civicrm_api("ContributionRecur", "create", $query); if (!empty($result['is_error'])) { CRM_Core_Session::setStatus(sprintf(ts("Cannot modify recurring contribution [%s]. Error was: '%s'"), $contribution_id, $result['error_message']), ts('Error'), 'error'); $lock->release(); return FALSE; } // find already created contributions. Those also need to be modified $contributions2adjust = array(); $adjusted_ids = array(); $find_need2adjust = "\n SELECT id\n FROM civicrm_contribution\n WHERE receive_date >= DATE(NOW())\n AND contribution_recur_id = {$contribution_id}\n AND contribution_status_id = {$contribution_id_pending};"; $contributions2adjust_query = CRM_Core_DAO::executeQuery($find_need2adjust); while ($contributions2adjust_query->fetch()) { $contributions2adjust[] = $contributions2adjust_query->id; } // ...and adjust them: foreach ($contributions2adjust as $contribution2adjust_id) { $update_result = civicrm_api("Contribution", "create", array('version' => 3, 'id' => $contribution2adjust_id, 'total_amount' => $adjusted_amount, 'contribution_status_id' => $contribution_id_pending)); if (!empty($update_result['is_error'])) { CRM_Core_Session::setStatus(sprintf(ts("Cannot update scheduled contribution [%s]. Error was: '%s'"), $contribution2adjust_id, $update_result['error_message']), ts('Error'), 'warn'); } else { array_push($adjusted_ids, $contribution2adjust_id); } } if (count($adjusted_ids)) { CRM_Core_Session::setStatus(sprintf(ts("Successfully updated %d generated contributions."), count($adjusted_ids)), ts('Mandate updated.'), 'info'); } $lock->release(); return TRUE; }
/** * This method will mark the given transaction group as 'received': * - set txgroup status to 'received' * - change status from 'In Progress' to 'Completed' for all contributions * - (store/update the bank account information) * * @return error message, unless successful */ static function received($txgroup_id) { // step 0: check lock $lock = CRM_Sepa_Logic_Settings::getLock(); if (empty($lock)) { return "Batching in progress. Please try again later."; } // step 1: gather data $group_status_id_open = (int) CRM_Core_OptionGroup::getValue('batch_status', 'Open', 'name'); $group_status_id_closed = (int) CRM_Core_OptionGroup::getValue('batch_status', 'Closed', 'name'); $group_status_id_received = (int) CRM_Core_OptionGroup::getValue('batch_status', 'Received', 'name'); $status_pending = (int) CRM_Core_OptionGroup::getValue('contribution_status', 'Pending', 'name'); $status_closed = (int) CRM_Core_OptionGroup::getValue('contribution_status', 'Completed', 'name'); $status_inprogress = (int) CRM_Core_OptionGroup::getValue('contribution_status', 'In Progress', 'name'); if (empty($group_status_id_received)) { return civicrm_api3_create_error("Status 'Received' does not exist!"); } // step 0: load the group object $txgroup = civicrm_api('SepaTransactionGroup', 'getsingle', array('id' => $txgroup_id, 'version' => 3)); if (!empty($txgroup['is_error'])) { $lock->release(); return "Cannot find transaction group " . $txgroup_id; } // check status if ($txgroup['status_id'] != $group_status_id_closed) { $lock->release(); return "Transaction group " . $txgroup_id . " is not 'closed'."; } // step 1.1: fix contributions, that have no financial transactions. (happens due to a status-bug in civicrm) $find_rotten_contributions_sql = "\n SELECT\n contribution.id AS contribution_id\n FROM\n civicrm_sdd_contribution_txgroup AS txn_to_contribution\n LEFT JOIN\n civicrm_contribution AS contribution ON contribution.id = txn_to_contribution.contribution_id\n WHERE\n txn_to_contribution.txgroup_id IN ({$txgroup_id})\n AND\n contribution.id NOT IN (SELECT entity_id FROM civicrm_entity_financial_trxn WHERE entity_table='civicrm_contribution');\n "; $rotten_contribution = CRM_Core_DAO::executeQuery($find_rotten_contributions_sql); while ($rotten_contribution->fetch()) { $contribution_id = $rotten_contribution->contribution_id; // set these rotten contributions to 'Pending', no 'pay_later' CRM_Core_DAO::executeQuery("UPDATE civicrm_contribution SET contribution_status_id={$status_pending}, is_pay_later=0 WHERE id={$contribution_id};"); // now they will get their transactions back when they get set to 'completed' in the next step... error_log("org.project60.sepa: reset bad contribution [{$contribution_id}] to 'Pending'."); } // step 1.2: in CiviCRM before 4.4.4, the status 'In Progress' => 'Completed' was not allowed: if (version_compare(CRM_Utils_System::version(), '4.4.4', '<')) { // therefore, we change all these contributions' statuses back to 'Pending' $fix_status_query = "\n UPDATE\n civicrm_contribution\n SET\n contribution_status_id = {$status_pending},\n is_pay_later = 0\n WHERE \n contribution_status_id = {$status_inprogress}\n AND id IN (SELECT contribution_id FROM civicrm_sdd_contribution_txgroup WHERE txgroup_id={$txgroup_id});\n "; CRM_Core_DAO::executeQuery($fix_status_query); } // step 2: update all the contributions $find_txgroup_contributions_sql = "\n SELECT\n contribution.id AS contribution_id\n FROM\n civicrm_sdd_contribution_txgroup AS txn_to_contribution\n LEFT JOIN\n civicrm_contribution AS contribution ON contribution.id = txn_to_contribution.contribution_id\n WHERE\n contribution_status_id != {$status_closed}\n AND\n txn_to_contribution.txgroup_id IN ({$txgroup_id});\n "; $contribution = CRM_Core_DAO::executeQuery($find_txgroup_contributions_sql); $error_count = 0; while ($contribution->fetch()) { // update status for $contribution->contribution_id // and set receive_date to collection_date (see https://github.com/Project60/sepa_dd/issues/190) $result = civicrm_api('Contribution', 'create', array('version' => 3, 'id' => $contribution->contribution_id, 'contribution_status_id' => $status_closed, 'receive_date' => date('YmdHis', strtotime($txgroup['collection_date'])))); if (!empty($result['is_error'])) { $error_count += 1; error_log("org.project60.sepa: " . $result['error_message']); } } // step 3: update group status $result = civicrm_api('SepaTransactionGroup', 'create', array('id' => $txgroup_id, 'status_id' => $group_status_id_received, 'version' => 3)); if (!empty($result['is_error'])) { $lock->release(); return "Cannot update transaction group status for ID " . $txgroup_id; } // check if there was problems if ($error_count) { $lock->release(); return "{$error_count} contributions could not be updated to status 'completed'."; } $lock->release(); }
/** * Maintenance: Close all mandates that have expired */ static function closeEnded() { // check lock $lock = CRM_Sepa_Logic_Settings::getLock(); if (empty($lock)) { return "Batching in progress. Please try again later."; } $contribution_status_closed = (int) CRM_Core_OptionGroup::getValue('contribution_status', 'Completed', 'name'); // first, load all of the mandates, that have run out $sql_query = "\n SELECT\n mandate.id AS mandate_id,\n mandate.date AS mandate_date,\n mandate.entity_id AS mandate_entity_id,\n mandate.creation_date AS mandate_creation_date,\n mandate.validation_date AS mandate_validation_date,\n rcontribution.end_date AS end_date\n FROM civicrm_sdd_mandate AS mandate\n INNER JOIN civicrm_contribution_recur AS rcontribution ON mandate.entity_id = rcontribution.id\n WHERE mandate.type = 'RCUR'\n AND mandate.status IN ('RCUR','FRST')\n AND end_date <= DATE(NOW());"; $results = CRM_Core_DAO::executeQuery($sql_query); $mandates_to_end = array(); while ($results->fetch()) { array_push($mandates_to_end, array('mandate_id' => $results->mandate_id, 'recur_id' => $results->mandate_entity_id, 'creation_date' => $results->mandate_creation_date, 'validation_date' => $results->mandate_validation_date, 'date' => $results->mandate_date)); } // then, end them one by one foreach ($mandates_to_end as $mandate_to_end) { $change_mandate = civicrm_api('SepaMandate', 'create', array('id' => $mandate_to_end['mandate_id'], 'date' => $mandate_to_end['date'], 'creation_date' => $mandate_to_end['creation_date'], 'validation_date' => $mandate_to_end['validation_date'], 'status' => 'COMPLETE', 'version' => 3)); if (isset($change_mandate['is_error']) && $change_mandate['is_error']) { $lock->release(); return sprintf("Couldn't set mandate '%s' to 'complete. Error was: '%s'", $mandates_to_end['mandate_id'], $change_mandate['error_message']); } $change_rcur = civicrm_api('ContributionRecur', 'create', array('id' => $mandate_to_end['recur_id'], 'contribution_status_id' => $contribution_status_closed, 'modified_date' => date('YmdHis'), 'currency' => 'EUR', 'version' => 3)); if (isset($change_rcur['is_error']) && $change_rcur['is_error']) { $lock->release(); return sprintf("Couldn't set recurring contribution '%s' to 'complete. Error was: '%s'", $mandates_to_end['recur_id'], $change_rcur['error_message']); } } $lock->release(); }