diff --git a/CRM/Admin/Form/Setting/SepaSettings.php b/CRM/Admin/Form/Setting/SepaSettings.php index cd77ac56..0933efa9 100644 --- a/CRM/Admin/Form/Setting/SepaSettings.php +++ b/CRM/Admin/Form/Setting/SepaSettings.php @@ -132,6 +132,7 @@ public function buildQuickForm( ) { // look up some values $async_batch = CRM_Sepa_Logic_Settings::getGenericSetting('sdd_async_batching'); + $financial_type_grouping = CRM_Sepa_Logic_Settings::getGenericSetting('sdd_financial_type_grouping'); $skip_closed = CRM_Sepa_Logic_Settings::getGenericSetting('sdd_skip_closed'); $no_draftxml = CRM_Sepa_Logic_Settings::getGenericSetting('sdd_no_draft_xml'); $excld_we = CRM_Sepa_Logic_Settings::getGenericSetting('exclude_weekends'); @@ -157,6 +158,7 @@ public function buildQuickForm( ) { $this->addElement('checkbox', 'is_test_creditor', E::ts("Is a Test Creditor"), "", array('value' =>'0')); $this->addElement('checkbox', 'exclude_weekends', E::ts("Exclude Weekends"), "", ($excld_we?array('checked'=>'checked'):array())); $this->addElement('checkbox', 'sdd_async_batching', E::ts("Large Groups"), "", ($async_batch?array('checked'=>'checked'):array())); + $this->addElement('checkbox', 'sdd_financial_type_grouping', E::ts('Groups by Financial Types'), "", ($financial_type_grouping?array('checked'=>'checked'):array())); $this->addElement('checkbox', 'sdd_skip_closed', E::ts("Only Completed Contributions"), "", ($skip_closed?array('checked'=>'checked'):array())); $this->addElement('checkbox', 'sdd_no_draft_xml', E::ts("No XML drafts"), "", ($no_draftxml?array('checked'=>'checked'):array())); $this->addElement('text', 'pp_buffer_days', E::ts("Buffer Days"), array('size' => 2, 'value' => $bffrdays)); @@ -252,6 +254,7 @@ function postProcess() { CRM_Sepa_Logic_Settings::setSetting((isset($values['exclude_weekends']) ? "1" : "0"), 'exclude_weekends'); CRM_Sepa_Logic_Settings::setSetting((isset($values['sdd_async_batching']) ? "1" : "0"), 'sdd_async_batching'); + CRM_Sepa_Logic_Settings::setSetting((isset($values['sdd_financial_type_grouping']) ? "1" : "0"), 'sdd_financial_type_grouping'); CRM_Sepa_Logic_Settings::setSetting((isset($values['sdd_skip_closed']) ? "1" : "0"), 'sdd_skip_closed'); CRM_Sepa_Logic_Settings::setSetting((isset($values['sdd_no_draft_xml']) ? "1" : "0"), 'sdd_no_draft_xml'); CRM_Sepa_Logic_Settings::setSetting((isset($values['pp_buffer_days']) ? (int) $values['pp_buffer_days'] : "0"), 'pp_buffer_days'); diff --git a/CRM/Sepa/BAO/SEPAMandate.php b/CRM/Sepa/BAO/SEPAMandate.php index fdbfc575..4e00413d 100644 --- a/CRM/Sepa/BAO/SEPAMandate.php +++ b/CRM/Sepa/BAO/SEPAMandate.php @@ -21,6 +21,7 @@ * */ +use CRM_Sepa_ExtensionUtil as E; /** * Class contains functions for Sepa mandates @@ -48,9 +49,14 @@ static function add(&$params) { // new mandate, use the default creditor $default_creditor = CRM_Sepa_Logic_Settings::defaultCreditor(); $params['creditor_id'] = $default_creditor->id; - } else { + } + else { // existing mandate, get creditor - $params['creditor_id'] = civicrm_api3('SepaMandate', 'getvalue', array ('id' => $params['id'], 'return' => 'creditor_id')); + $params['creditor_id'] = \Civi\Api4\SepaMandate::get(TRUE) + ->addSelect('creditor_id') + ->addWhere('id', '=', $params['id']) + ->execute() + ->single()['creditor_id']; } } $creditor = civicrm_api3 ('SepaCreditor', 'getsingle', array ('id' => $params['creditor_id'], 'return' => 'mandate_prefix,creditor_type')); @@ -371,16 +377,22 @@ static function terminateMandate($mandate_id, $new_end_date_str, $cancel_reason= } // first, load the mandate - $mandate = civicrm_api("SepaMandate", "getsingle", array('id'=>$mandate_id, 'version'=>3)); - if (isset($mandate['is_error'])) { + try { + $mandate = \Civi\Api4\SepaMandate::get(TRUE) + ->addWhere('id', '=', $mandate_id) + ->execute() + ->single(); + } + catch (\CRM_Core_Exception $exception) { $lock->release(); - $error_message = sprintf(ts("Cannot read mandate [%s]. Error was: '%s'", array('domain' => 'org.project60.sepa')), $mandate_id, $mandate['error_message']); + $error_message = E::ts("Cannot read mandate [%1]. Error was: '%2'", [1 => $mandate_id, 2 => $exception->getMessage()]); if ($error_to_ui) { CRM_Core_Session::setStatus($error_message, ts('Error'), 'error'); return FALSE; - } else { - throw new Exception($error_message); + } + else { + throw new CRM_Core_Exception($error_message); } } @@ -546,7 +558,10 @@ static function modifyMandate($mandate_id, $changes) { } // load the mandate - $mandate = civicrm_api3('SepaMandate', 'getsingle', array('id' => $mandate_id)); + $mandate = \Civi\Api4\SepaMandate::get(TRUE) + ->addWhere('id', '=', $mandate_id) + ->execute() + ->single(); if ($mandate['type'] != 'RCUR') { $lock->release(); throw new Exception(ts("You can only modify RCUR mandates.", array('domain' => 'org.project60.sepa'))); @@ -906,7 +921,10 @@ public static function getLastMandateOfContact($cid) { ]); if ($result->fetch()) { // return the mandate - return civicrm_api3('SepaMandate', 'getsingle', array('id' => $result->mandate_id)); + return \Civi\Api4\SepaMandate::get(TRUE) + ->addWhere('id', '=', $result->mandate_id) + ->execute() + ->single(); } else { return FALSE; diff --git a/CRM/Sepa/BAO/SEPATransactionGroup.php b/CRM/Sepa/BAO/SEPATransactionGroup.php index 740c59da..9f632be5 100644 --- a/CRM/Sepa/BAO/SEPATransactionGroup.php +++ b/CRM/Sepa/BAO/SEPATransactionGroup.php @@ -184,8 +184,13 @@ function generateXML ($id = null) { * @return int id of the sepa file entity created, or an error message string */ static function createFile($txgroup_id, $override = false) { - $txgroup = civicrm_api('SepaTransactionGroup', 'getsingle', array('id'=>$txgroup_id, 'version'=>3)); - if (isset($txgroup['is_error']) && $txgroup['is_error']) { + try { + $txgroup = \Civi\Api4\SepaTransactionGroup::get(TRUE) + ->addWhere('id', '=', $txgroup_id) + ->execute() + ->single(); + } + catch (CRM_Core_Exception $exception) { return "Cannot find transaction group ".$txgroup_id; } @@ -252,7 +257,10 @@ static function createFile($txgroup_id, $override = false) { * @return an update array with the txgroup or a string with an error message */ static function adjustCollectionDate($txgroup_id, $latest_submission_date) { - $txgroup = civicrm_api3('SepaTransactionGroup', 'getsingle', array('id' => $txgroup_id)); + $txgroup = \Civi\Api4\SepaTransactionGroup::get(TRUE) + ->addWhere('id', '=', $txgroup_id) + ->execute() + ->single(); if ($txgroup['type'] == 'RTRY') { $txgroup['type'] = 'RCUR'; } @@ -277,12 +285,16 @@ static function adjustCollectionDate($txgroup_id, $latest_submission_date) { } // reload the item - $txgroup = civicrm_api('SepaTransactionGroup', 'getsingle', array('version'=>3, 'id'=>$txgroup_id)); - if (!empty($txgroup['is_error'])) { - return $txgroup['error_message']; - } else { - return $txgroup; + try { + $txgroup = \Civi\Api4\SepaTransactionGroup::get(TRUE) + ->addWhere('id', '=', $txgroup_id) + ->execute() + ->single(); } + catch (CRM_Core_Exception $exception) { + return $exception->getMessage(); + } + return $txgroup; } @@ -302,9 +314,14 @@ static function adjustCollectionDate($txgroup_id, $latest_submission_date) { */ static function deleteGroup($txgroup_id, $delete_contributions_mode = 'no') { // load the group - $txgroup = civicrm_api('SepaTransactionGroup', 'getsingle', array('id' => $txgroup_id, 'version' => 3)); - if (!empty($txgroup['is_error'])) { - return "Transaction group [$txgroup_id] could not be loaded. Error was: ".$txgroup['error_message']; + try { + $txgroup = \Civi\Api4\SepaTransactionGroup::get(TRUE) + ->addWhere('id', '=', $txgroup_id) + ->execute() + ->single(); + } + catch (\CRM_Core_Exception $exception) { + return "Transaction group [$txgroup_id] could not be loaded. Error was: " . $exception->getMessage(); } // first, delete the contents of this group diff --git a/CRM/Sepa/DAO/SEPATransactionGroup.php b/CRM/Sepa/DAO/SEPATransactionGroup.php index 54e812b1..ad86ab6e 100644 --- a/CRM/Sepa/DAO/SEPATransactionGroup.php +++ b/CRM/Sepa/DAO/SEPATransactionGroup.php @@ -67,6 +67,16 @@ class CRM_Sepa_DAO_SEPATransactionGroup extends CRM_Core_DAO { */ public $collection_date; + /** + * Financial type of contained contributions if CiviSEPA is generating groups + * matching financial types. + * + * @var int|string|null + * (SQL type: int unsigned) + * Note that values will be retrieved from the database as a string. + */ + public $financial_type_id; + /** * Latest submission date * @@ -233,6 +243,25 @@ public static function &fields() { 'localizable' => 0, 'add' => NULL, ], + 'financial_type_id' => [ + 'name' => 'financial_type_id', + 'type' => CRM_Utils_Type::T_INT, + 'title' => E::ts('Financial Type ID'), + 'description' => E::ts('Financial type of contained contributions if CiviSEPA is generating groups matching financial types.'), + 'required' => FALSE, + 'usage' => [ + 'import' => FALSE, + 'export' => FALSE, + 'duplicate_matching' => FALSE, + 'token' => FALSE, + ], + 'where' => 'civicrm_sdd_txgroup.financial_type_id', + 'table_name' => 'civicrm_sdd_txgroup', + 'entity' => 'SEPATransactionGroup', + 'bao' => 'CRM_Sepa_DAO_SEPATransactionGroup', + 'localizable' => 0, + 'add' => NULL, + ], 'latest_submission_date' => [ 'name' => 'latest_submission_date', 'type' => CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME, diff --git a/CRM/Sepa/Form/CreateMandate.php b/CRM/Sepa/Form/CreateMandate.php index 77395528..41194ac8 100644 --- a/CRM/Sepa/Form/CreateMandate.php +++ b/CRM/Sepa/Form/CreateMandate.php @@ -13,6 +13,8 @@ | copyright header is strictly prohibited without | | written permission from the original author(s). | +--------------------------------------------------------*/ + +use Civi\Sepa\Util\ContributionUtil; use CRM_Sepa_ExtensionUtil as E; /** @@ -52,9 +54,21 @@ public function buildQuickForm() { } // load mandate - $this->old_mandate = civicrm_api3('SepaMandate', 'getsingle', array('id' => $mandate_id)); + try { + // API4 SepaMandate.get checks Financial ACLs for corresponding (recurring) contribution. + $this->old_mandate = \Civi\Api4\SepaMandate::get(TRUE) + ->addWhere('id', '=', $mandate_id) + ->execute() + ->single(); + } + catch (\Exception $e) { + Civi::log()->error($e->getMessage()); + CRM_Core_Error::statusBounce( + E::ts('The mandate to clone/replace from does not exist or you do not have permission for it.'), + ); + } if ($this->old_mandate['type'] != 'RCUR') { - CRM_Core_Error::fatal(E::ts("You can only replace RCUR mandates")); + CRM_Core_Error::statusBounce(E::ts('You can only replace RCUR mandates')); } $this->contact_id = (int) $this->old_mandate['contact_id']; @@ -62,7 +76,7 @@ public function buildQuickForm() { } if (empty($this->contact_id)) { - CRM_Core_Error::fatal(E::ts("No contact ID (cid) given.")); + CRM_Core_Error::statusBounce(E::ts("No contact ID (cid) given.")); } // load the contact and set the title @@ -111,7 +125,7 @@ public function buildQuickForm() { 'select', 'payment_instrument_id', E::ts('Payment Method'), - $this->getPaymentInstrumentList(), + ContributionUtil::getPaymentInstrumentList(), TRUE, array('class' => 'crm-select2') ); @@ -121,7 +135,7 @@ public function buildQuickForm() { 'select', 'financial_type_id', E::ts('Financial Type'), - $this->getFinancialTypeList(), + ContributionUtil::getFinancialTypeList(), TRUE, array('class' => 'crm-select2') ); @@ -455,9 +469,11 @@ public function postProcess() { try { $mandate = civicrm_api3('SepaMandate', 'createfull', $mandate_data); - $mandate = civicrm_api3('SepaMandate', 'getsingle', array( - 'id' => $mandate['id'], - 'return' => 'reference,id,type')); + $mandate = \Civi\Api4\SepaMandate::get(TRUE) + ->addSelect('reference', 'id', 'type') + ->addWhere('id', '=', $mandate['id']) + ->execute() + ->single(); // if we get here, everything went o.k. CRM_Core_Session::setStatus(E::ts("'%3' SEPA Mandate %1 created.", array( @@ -469,7 +485,10 @@ public function postProcess() { // terminate old mandate, of requested if (!empty($values['replace'])) { - $rpl_mandate = civicrm_api3('SepaMandate', 'getsingle', array('id' => $values['replace'])); + $rpl_mandate = \Civi\Api4\SepaMandate::get(TRUE) + ->addWhere('id', '=', $values['replace']) + ->execute() + ->single(); CRM_Sepa_BAO_SEPAMandate::terminateMandate( $values['replace'], @@ -572,37 +591,6 @@ protected function getCreditorList($creditors) { return $creditor_list; } - /** - * Get the list of (active) financial types - */ - protected function getFinancialTypeList() { - $list = array(); - $query = civicrm_api3('FinancialType', 'get',array( - 'is_active' => 1, - 'option.limit' => 0, - 'return' => 'id,name' - )); - - foreach ($query['values'] as $value) { - $list[$value['id']] = $value['name']; - } - - return $list; - } - - /** - * Get the list of (CiviSEPA) payment instruments - */ - protected function getPaymentInstrumentList() { - $list = array(); - $payment_instruments = CRM_Sepa_Logic_PaymentInstruments::getAllSddPaymentInstruments(); - foreach ($payment_instruments as $payment_instrument) { - $list[$payment_instrument['id']] = $payment_instrument['label']; - } - - return $list; - } - /** * Get the list of (active) financial types */ @@ -631,14 +619,14 @@ protected function getKnownBankAccounts() { $known_accounts = array('' => E::ts("new account")); // get data from SepaMandates - $mandates = civicrm_api3('SepaMandate', 'get', array( - 'contact_id' => $this->contact_id, - 'status' => array('IN' => array('RCUR', 'COMPLETE', 'SENT')), - 'option.limit' => 0, - 'return' => 'iban,bic,reference', - 'option.sort' => 'id desc' - )); - foreach ($mandates['values'] as $mandate) { + // API4 SepaMandate.get checks Financial ACLs for corresponding (recurring) contribution. + $mandates = \Civi\Api4\SepaMandate::get(TRUE) + ->addSelect('iban', 'bic', 'reference') + ->addWhere('contact_id', '=', $this->contact_id) + ->addWhere('status', 'IN', ['RCUR', 'COMPLETE', 'SENT']) + ->addOrderBy('id', 'DESC') + ->execute(); + foreach ($mandates as $mandate) { $key = "{$mandate['iban']}/{$mandate['bic']}"; if (!isset($known_accounts[$key])) { $known_accounts[$key] = "{$mandate['iban']} ({$mandate['reference']})"; diff --git a/CRM/Sepa/Form/RetryCollection.php b/CRM/Sepa/Form/RetryCollection.php index 6f9798ad..08895f55 100644 --- a/CRM/Sepa/Form/RetryCollection.php +++ b/CRM/Sepa/Form/RetryCollection.php @@ -221,15 +221,12 @@ protected function getCreditorList() { * Get the list of creditors */ protected function getGroupList() { - $txgroup_list = array(); - $txgroup_query = civicrm_api3('SepaTransactionGroup', 'get', array( - 'option.limit' => 0, - 'type' => array('IN' => array('RCUR', 'FRST')), - 'return' => 'reference,id')); - foreach ($txgroup_query['values'] as $txgroup) { - $txgroup_list[$txgroup['id']] = $txgroup['reference']; - } - return $txgroup_list; + return \Civi\Api4\SepaTransactionGroup::get(TRUE) + ->addSelect('reference', 'id') + ->addWhere('type', 'IN', ['RCUR', 'FRST']) + ->execute() + ->indexBy('id') + ->column('reference'); } /* diff --git a/CRM/Sepa/Logic/Batching.php b/CRM/Sepa/Logic/Batching.php index f2bbf30f..4523b060 100644 --- a/CRM/Sepa/Logic/Batching.php +++ b/CRM/Sepa/Logic/Batching.php @@ -34,6 +34,7 @@ static function updateRCUR($creditor_id, $mode, $now = 'now', $offset=NULL, $lim if (empty($lock)) { return "Batching in progress. Please try again later."; } + $horizon = (int) CRM_Sepa_Logic_Settings::getSetting("batching.RCUR.horizon", $creditor_id); $grace_period = (int) CRM_Sepa_Logic_Settings::getSetting("batching.RCUR.grace", $creditor_id); $latest_date = date('Y-m-d', strtotime("$now +$horizon days")); @@ -54,85 +55,103 @@ static function updateRCUR($creditor_id, $mode, $now = 'now', $offset=NULL, $lim if ($offset !== NULL && $limit!==NULL) { $batch_clause = "LIMIT {$limit} OFFSET {$offset}"; - } else { + } + else { $batch_clause = ""; } // RCUR-STEP 0: check/repair mandates + // TODO: Does this need changes for Financial ACLs? CRM_Sepa_Logic_MandateRepairs::runWithMandateSelector( "mandate.type = 'RCUR' AND mandate.status = '{$mode}' AND mandate.creditor_id = {$creditor_id} {$batch_clause}", true ); - // RCUR-STEP 1: find all active/pending RCUR mandates within the horizon that are NOT in a closed batch - $sql_query = " - SELECT - mandate.id AS mandate_id, - mandate.contact_id AS mandate_contact_id, - mandate.entity_id AS mandate_entity_id, - mandate.source AS mandate_source, - mandate.creditor_id AS mandate_creditor_id, - first_contribution.receive_date AS mandate_first_executed, - rcontribution.cycle_day AS cycle_day, - rcontribution.frequency_interval AS frequency_interval, - rcontribution.frequency_unit AS frequency_unit, - rcontribution.start_date AS start_date, - rcontribution.cancel_date AS cancel_date, - rcontribution.end_date AS end_date, - rcontribution.amount AS rc_amount, - rcontribution.is_test AS rc_is_test, - rcontribution.contact_id AS rc_contact_id, - rcontribution.financial_type_id AS rc_financial_type_id, - rcontribution.contribution_status_id AS rc_contribution_status_id, - rcontribution.currency AS rc_currency, - rcontribution.campaign_id AS rc_campaign_id, - rcontribution.payment_instrument_id AS rc_payment_instrument_id - FROM civicrm_sdd_mandate AS mandate - INNER JOIN civicrm_contribution_recur AS rcontribution ON mandate.entity_id = rcontribution.id AND mandate.entity_table = 'civicrm_contribution_recur' - LEFT JOIN civicrm_contribution AS first_contribution ON mandate.first_contribution_id = first_contribution.id - WHERE mandate.type = 'RCUR' - AND mandate.status = '{$mode}' - AND mandate.creditor_id = {$creditor_id} - {$batch_clause};"; - $results = CRM_Core_DAO::executeQuery($sql_query); - $relevant_mandates = array(); - while ($results->fetch()) { - // TODO: sanity checks? - $relevant_mandates[$results->mandate_id] = array( - 'mandate_id' => $results->mandate_id, - 'mandate_contact_id' => $results->mandate_contact_id, - 'mandate_entity_id' => $results->mandate_entity_id, - 'mandate_first_executed' => $results->mandate_first_executed, - 'mandate_source' => $results->mandate_source, - 'mandate_creditor_id' => $results->mandate_creditor_id, - 'cycle_day' => $results->cycle_day, - 'frequency_interval' => $results->frequency_interval, - 'frequency_unit' => $results->frequency_unit, - 'start_date' => $results->start_date, - 'end_date' => $results->end_date, - 'cancel_date' => $results->cancel_date, - 'rc_contact_id' => $results->rc_contact_id, - 'rc_amount' => $results->rc_amount, - 'rc_currency' => $results->rc_currency, - 'rc_financial_type_id' => $results->rc_financial_type_id, - 'rc_contribution_status_id' => $results->rc_contribution_status_id, - 'rc_campaign_id' => $results->rc_campaign_id, - 'rc_payment_instrument_id' => $results->rc_payment_instrument_id, - 'rc_is_test' => $results->rc_is_test, - ); + // RCUR-STEP 1: find all active/pending RCUR mandates within the horizon that are NOT in a closed batch and that + // have a corresponding contribution of a financial type the user has access to (implicit condition added by + // Financial ACLs extension if enabled) + $relevant_mandates = \Civi\Api4\SepaMandate::get(TRUE) + ->addSelect( + 'id', + 'contact_id', + 'entity_id', + 'source', + 'creditor_id', + 'first_contribution.receive_date', + 'contribution_recur.cycle_day', + 'contribution_recur.frequency_interval', + 'contribution_recur.frequency_unit', + 'contribution_recur.start_date', + 'contribution_recur.cancel_date', + 'contribution_recur.end_date', + 'contribution_recur.amount', + 'contribution_recur.is_test', + 'contribution_recur.contact_id', + 'contribution_recur.financial_type_id', + 'contribution_recur.contribution_status_id', + 'contribution_recur.currency', + 'contribution_recur.campaign_id', + 'contribution_recur.payment_instrument_id' + ) + ->addJoin( + 'ContributionRecur AS contribution_recur', + 'INNER', + ['entity_table', '=', '"civicrm_contribution_recur"'], + ['entity_id', '=', 'contribution_recur.id'] + ) + ->addJoin( + 'Contribution AS first_contribution', + 'LEFT', + ['first_contribution_id', '=', 'first_contribution.id'] + ) + ->addWhere('type', '=', 'RCUR') + ->addWhere('status', '=', $mode) + ->addWhere('creditor_id', '=', $creditor_id) + ->setLimit($limit) + ->setOffset($offset) + ->execute() + ->getArrayCopy(); + + foreach ($relevant_mandates as &$mandate) { + $mandate += [ + 'mandate_id' => $mandate['id'], + 'mandate_contact_id' => $mandate['contact_id'], + 'mandate_entity_id' => $mandate['entity_id'], + 'mandate_first_executed' => $mandate['first_contribution.receive_date'], + 'mandate_source' => $mandate['source'], + 'mandate_creditor_id' => $mandate['creditor_id'], + 'cycle_day' => $mandate['contribution_recur.cycle_day'], + 'frequency_interval' => $mandate['contribution_recur.frequency_interval'], + 'frequency_unit' => $mandate['contribution_recur.frequency_unit'], + 'start_date' => $mandate['contribution_recur.start_date'], + 'end_date' => $mandate['contribution_recur.end_date'], + 'cancel_date' => $mandate['contribution_recur.cancel_date'], + 'rc_contact_id' => $mandate['contribution_recur.contact_id'], + 'rc_amount' => $mandate['contribution_recur.amount'], + 'rc_currency' => $mandate['contribution_recur.currency'], + 'rc_financial_type_id' => $mandate['contribution_recur.financial_type_id'], + 'rc_contribution_status_id' => $mandate['contribution_recur.contribution_status_id'], + 'rc_campaign_id' => $mandate['contribution_recur.campaign_id'] ?? NULL, + 'rc_payment_instrument_id' => $mandate['contribution_recur.payment_instrument_id'], + 'rc_is_test' => $mandate['contribution_recur.is_test'], + ]; } // RCUR-STEP 2: calculate next execution date - $mandates_by_nextdate = array(); + $mandates_by_nextdate = []; foreach ($relevant_mandates as $mandate) { $next_date = self::getNextExecutionDate($mandate, $now, ($mode=='FRST')); - if ($next_date==NULL) continue; - if ($next_date > $latest_date) continue; - - if (!isset($mandates_by_nextdate[$next_date])) - $mandates_by_nextdate[$next_date] = array(); - array_push($mandates_by_nextdate[$next_date], $mandate); + if (NULL === $next_date || $next_date > $latest_date) { + continue; + } + if (!isset($mandates_by_nextdate[$next_date])) { + $mandates_by_nextdate[$next_date] = []; + } + if (!isset($mandates_by_nextdate[$next_date][$mandate['rc_financial_type_id']])) { + $mandates_by_nextdate[$next_date][$mandate['rc_financial_type_id']] = []; + } + array_push($mandates_by_nextdate[$next_date][$mandate['rc_financial_type_id']], $mandate); } // apply any deferrals: $collection_dates = array_keys($mandates_by_nextdate); @@ -142,8 +161,12 @@ static function updateRCUR($creditor_id, $mode, $now = 'now', $offset=NULL, $lim if ($deferred_collection_date != $collection_date) { if (empty($mandates_by_nextdate[$deferred_collection_date])) { $mandates_by_nextdate[$deferred_collection_date] = $mandates_by_nextdate[$collection_date]; - } else { - $mandates_by_nextdate[$deferred_collection_date] = array_merge($mandates_by_nextdate[$collection_date], $mandates_by_nextdate[$deferred_collection_date]); + } + else { + $mandates_by_nextdate[$deferred_collection_date] = array_merge( + $mandates_by_nextdate[$collection_date], + $mandates_by_nextdate[$deferred_collection_date] + ); } unset($mandates_by_nextdate[$collection_date]); } @@ -151,15 +174,16 @@ static function updateRCUR($creditor_id, $mode, $now = 'now', $offset=NULL, $lim // RCUR-STEP 3: find already created contributions - $existing_contributions_by_recur_id = array(); - foreach ($mandates_by_nextdate as $collection_date => $mandates) { - $rcontrib_ids = array(); - foreach ($mandates as $mandate) { - array_push($rcontrib_ids, $mandate['mandate_entity_id']); - } - $rcontrib_id_strings = implode(',', $rcontrib_ids); + $existing_contributions_by_recur_id = []; + foreach ($mandates_by_nextdate as $collection_date => $financial_type_mandates) { + foreach ($financial_type_mandates as $financial_type => $mandates) { + $rcontrib_ids = []; + foreach ($mandates as $mandate) { + array_push($rcontrib_ids, $mandate['mandate_entity_id']); + } + $rcontrib_id_strings = implode(',', $rcontrib_ids); - $sql_query = " + $sql_query = " SELECT contribution.contribution_recur_id AS contribution_recur_id, contribution.id AS contribution_id @@ -170,26 +194,29 @@ static function updateRCUR($creditor_id, $mode, $now = 'now', $offset=NULL, $lim AND DATE(contribution.receive_date) = DATE('{$collection_date}') AND (txg.type IS NULL OR txg.type IN ('RCUR', 'FRST')) AND contribution.payment_instrument_id IN ({$payment_instrument_id_list});"; - $results = CRM_Core_DAO::executeQuery($sql_query); - while ($results->fetch()) { - $existing_contributions_by_recur_id[$results->contribution_recur_id] = $results->contribution_id; + $results = CRM_Core_DAO::executeQuery($sql_query); + while ($results->fetch()) { + $existing_contributions_by_recur_id[$results->contribution_recur_id] = $results->contribution_id; + } } } // RCUR-STEP 4: create the missing contributions, store all in $mandate['mandate_entity_id'] - foreach ($mandates_by_nextdate as $collection_date => $mandates) { - foreach ($mandates as $index => $mandate) { - $recur_id = $mandate['mandate_entity_id']; - if (isset($existing_contributions_by_recur_id[$recur_id])) { - // if the contribution already exists, store it - $contribution_id = $existing_contributions_by_recur_id[$recur_id]; - unset($existing_contributions_by_recur_id[$recur_id]); - $mandates_by_nextdate[$collection_date][$index]['mandate_entity_id'] = $contribution_id; - } else { - // else: create it - $installment_pi = CRM_Sepa_Logic_PaymentInstruments::getInstallmentPaymentInstrument( - $creditor_id, $mandate['rc_payment_instrument_id'], ($mode == 'FRST')); - $contribution_data = array( + foreach ($mandates_by_nextdate as $collection_date => $financial_type_mandates) { + foreach ($financial_type_mandates as $financial_type => $mandates) { + foreach ($mandates as $index => $mandate) { + $recur_id = $mandate['mandate_entity_id']; + if (isset($existing_contributions_by_recur_id[$recur_id])) { + // if the contribution already exists, store it + $contribution_id = $existing_contributions_by_recur_id[$recur_id]; + unset($existing_contributions_by_recur_id[$recur_id]); + $mandates_by_nextdate[$collection_date][$financial_type][$index]['mandate_entity_id'] = $contribution_id; + } + else { + // else: create it + $installment_pi = CRM_Sepa_Logic_PaymentInstruments::getInstallmentPaymentInstrument( + $creditor_id, $mandate['rc_payment_instrument_id'], ($mode == 'FRST')); + $contribution_data = array( "version" => 3, "total_amount" => $mandate['rc_amount'], "currency" => $mandate['rc_currency'], @@ -203,25 +230,27 @@ static function updateRCUR($creditor_id, $mode, $now = 'now', $offset=NULL, $lim "is_test" => $mandate['rc_is_test'], "payment_instrument_id" => $installment_pi ); - $contribution = civicrm_api('Contribution', 'create', $contribution_data); - if (empty($contribution['is_error'])) { - // Success! Call the post_create hook - CRM_Utils_SepaCustomisationHooks::installment_created($mandate['mandate_id'], $recur_id, $contribution['id']); - - // 'mandate_entity_id' will now be overwritten with the contribution instance ID - // to allow compatibility in with OOFF groups in the syncGroups function - $mandates_by_nextdate[$collection_date][$index]['mandate_entity_id'] = $contribution['id']; - } else { - // in case of an error, we will unset 'mandate_entity_id', so it cannot be - // interpreted as the contribution instance ID (see above) - unset($mandates_by_nextdate[$collection_date][$index]['mandate_entity_id']); - - // log the error - Civi::log()->debug("org.project60.sepa: batching:updateRCUR/createContrib ".$contribution['error_message']); - - // TODO: Error handling? + $contribution = civicrm_api('Contribution', 'create', $contribution_data); + if (empty($contribution['is_error'])) { + // Success! Call the post_create hook + CRM_Utils_SepaCustomisationHooks::installment_created($mandate['mandate_id'], $recur_id, $contribution['id']); + + // 'mandate_entity_id' will now be overwritten with the contribution instance ID + // to allow compatibility in with OOFF groups in the syncGroups function + $mandates_by_nextdate[$collection_date][$financial_type][$index]['mandate_entity_id'] = $contribution['id']; + } + else { + // in case of an error, we will unset 'mandate_entity_id', so it cannot be + // interpreted as the contribution instance ID (see above) + unset($mandates_by_nextdate[$collection_date][$financial_type][$index]['mandate_entity_id']); + + // log the error + Civi::log()->debug("org.project60.sepa: batching:updateRCUR/createContrib ".$contribution['error_message']); + + // TODO: Error handling? + } + unset($existing_contributions_by_recur_id[$recur_id]); } - unset($existing_contributions_by_recur_id[$recur_id]); } } } @@ -236,20 +265,30 @@ static function updateRCUR($creditor_id, $mode, $now = 'now', $offset=NULL, $lim $sql_query = " SELECT txgroup.collection_date AS collection_date, + txgroup.financial_type_id AS financial_type_id, txgroup.id AS txgroup_id FROM civicrm_sdd_txgroup AS txgroup WHERE txgroup.type = '$mode' AND txgroup.sdd_creditor_id = $creditor_id AND txgroup.status_id = $group_status_id_open;"; $results = CRM_Core_DAO::executeQuery($sql_query); - $existing_groups = array(); + $existing_groups = []; while ($results->fetch()) { $collection_date = date('Y-m-d', strtotime($results->collection_date)); - $existing_groups[$collection_date] = $results->txgroup_id; + $existing_groups[$collection_date][$results->financial_type_id ?? 0] = $results->txgroup_id; } // step 6: sync calculated group structure with existing (open) groups - self::syncGroups($mandates_by_nextdate, $existing_groups, $mode, 'RCUR', $rcur_notice, $creditor_id, $offset!==NULL, $offset===0); + self::syncGroups( + $mandates_by_nextdate, + $existing_groups, + $mode, + 'RCUR', + $rcur_notice, + $creditor_id, + NULL !== $offset, + 0 === $offset + ); $lock->release(); } @@ -264,66 +303,59 @@ static function updateRCUR($creditor_id, $mode, $now = 'now', $offset=NULL, $lim * @param $offset used for segmented updates * @param $limit used for segmented updates */ - static function updateOOFF($creditor_id, $now = 'now', $offset=NULL, $limit=NULL) { + static function updateOOFF($creditor_id, $now = 'now', $offset = NULL, $limit = NULL) { // check lock $lock = CRM_Sepa_Logic_Settings::getLock(); if (empty($lock)) { return "Batching in progress. Please try again later."; } - if ($offset !== NULL && $limit!==NULL) { - $batch_clause = "LIMIT {$limit} OFFSET {$offset}"; - } else { - $batch_clause = ""; - } - $horizon = (int) CRM_Sepa_Logic_Settings::getSetting('batching.OOFF.horizon', $creditor_id); $ooff_notice = (int) CRM_Sepa_Logic_Settings::getSetting('batching.OOFF.notice', $creditor_id); $group_status_id_open = (int) CRM_Core_PseudoConstant::getKey('CRM_Batch_BAO_Batch', 'status_id', 'Open'); $date_limit = date('Y-m-d', strtotime("$now +$horizon days")); - // step 1: find all active/pending OOFF mandates within the horizon that are NOT in a closed batch - $sql_query = " - SELECT - mandate.id AS mandate_id, - mandate.contact_id AS mandate_contact_id, - mandate.entity_id AS mandate_entity_id, - contribution.receive_date AS start_date - FROM civicrm_sdd_mandate AS mandate - INNER JOIN civicrm_contribution AS contribution ON mandate.entity_id = contribution.id AND mandate.entity_table = 'civicrm_contribution' - WHERE contribution.receive_date <= DATE('$date_limit') - AND mandate.type = 'OOFF' - AND mandate.status = 'OOFF' - AND mandate.creditor_id = $creditor_id - {$batch_clause};"; - $results = CRM_Core_DAO::executeQuery($sql_query); - $relevant_mandates = array(); - while ($results->fetch()) { - // TODO: sanity checks? - $relevant_mandates[$results->mandate_id] = array( - 'mandate_id' => $results->mandate_id, - 'mandate_contact_id' => $results->mandate_contact_id, - 'mandate_entity_id' => $results->mandate_entity_id, - 'start_date' => $results->start_date, - ); - } + // step 1: find all active/pending OOFF mandates within the horizon that are NOT in a closed batch and that have a + // corresponding contribution of a financial type the user has access to (implicit condition added by Financial ACLs + // extension if enabled). + $relevant_mandates = \Civi\Api4\SepaMandate::get(TRUE) + ->addSelect('id', 'contact_id', 'entity_id', 'contribution.receive_date', 'contribution.financial_type_id') + ->addJoin( + 'Contribution AS contribution', + 'INNER', + ['entity_table', '=', "'civicrm_contribution'"], + ['entity_id', '=', 'contribution.id'] + ) + ->addWhere('contribution.receive_date', '<=', $date_limit) + ->addWhere('type', '=', 'OOFF') + ->addWhere('status', '=', 'OOFF') + ->addWhere('creditor_id', '=', $creditor_id) + ->setLimit($limit ?? 0) + ->setOffset($offset ?? 0) + ->execute() + ->getArrayCopy(); // step 2: group mandates in collection dates - $calculated_groups = array(); + $calculated_groups = []; $earliest_collection_date = date('Y-m-d', strtotime("$now +$ooff_notice days")); $latest_collection_date = ''; - foreach ($relevant_mandates as $mandate_id => $mandate) { + foreach ($relevant_mandates as $mandate) { + $mandate['mandate_id'] = $mandate['id']; + $mandate['mandate_contact_id'] = $mandate['contact_id']; + $mandate['mandate_entity_id'] = $mandate['entity_id']; + $mandate['start_date'] = $mandate['contribution.receive_date']; + $mandate['financial_type_id'] = $mandate['contribution.financial_type_id']; $collection_date = date('Y-m-d', strtotime($mandate['start_date'])); if ($collection_date <= $earliest_collection_date) { $collection_date = $earliest_collection_date; } - if (!isset($calculated_groups[$collection_date])) { - $calculated_groups[$collection_date] = array(); + if (!isset($calculated_groups[$collection_date][$mandate['financial_type_id']])) { + $calculated_groups[$collection_date][$mandate['financial_type_id']] = []; } - array_push($calculated_groups[$collection_date], $mandate); + array_push($calculated_groups[$collection_date][$mandate['financial_type_id']], $mandate); if ($collection_date > $latest_collection_date) { $latest_collection_date = $collection_date; @@ -331,13 +363,14 @@ static function updateOOFF($creditor_id, $now = 'now', $offset=NULL, $limit=NULL } if (!$latest_collection_date) { // nothing to do... - return array(); + return []; } // step 3: find all existing OPEN groups in the horizon $sql_query = " SELECT txgroup.collection_date AS collection_date, + txgroup.financial_type_id AS financial_type_id, txgroup.id AS txgroup_id FROM civicrm_sdd_txgroup AS txgroup WHERE txgroup.sdd_creditor_id = $creditor_id @@ -347,11 +380,20 @@ static function updateOOFF($creditor_id, $now = 'now', $offset=NULL, $limit=NULL $existing_groups = array(); while ($results->fetch()) { $collection_date = date('Y-m-d', strtotime($results->collection_date)); - $existing_groups[$collection_date] = $results->txgroup_id; + $existing_groups[$collection_date][$results->financial_type_id ?? 0] = $results->txgroup_id; } // step 4: sync calculated group structure with existing (open) groups - self::syncGroups($calculated_groups, $existing_groups, 'OOFF', 'OOFF', $ooff_notice, $creditor_id, $offset!==NULL, $offset===0); + self::syncGroups( + $calculated_groups, + $existing_groups, + 'OOFF', + 'OOFF', + $ooff_notice, + $creditor_id, + $offset !== NULL, + $offset === 0 + ); $lock->release(); } @@ -438,6 +480,57 @@ static function closeEnded() { ** ** ****************************************************************************/ + public static function getOrCreateTransactionGroup( + int $creditor_id, + string $mode, + string $collection_date, + ?int $financial_type_id, + int $notice, + array &$existing_groups + ): int { + $group_status_id_open = (int) CRM_Core_PseudoConstant::getKey('CRM_Batch_BAO_Batch', 'status_id', 'Open'); + + if (!isset($existing_groups[$collection_date][$financial_type_id ?? 0])) { + // this group does not yet exist -> create + + // find unused reference + $reference = self::getTransactionGroupReference($creditor_id, $mode, $collection_date, $financial_type_id); + + $group = civicrm_api('SepaTransactionGroup', 'create', array( + 'version' => 3, + 'reference' => $reference, + 'type' => $mode, + 'collection_date' => $collection_date, + // Financial type may be NULL if not grouping by financial type. + 'financial_type_id' => $financial_type_id, + 'latest_submission_date' => date('Y-m-d', strtotime("-$notice days", strtotime($collection_date))), + 'created_date' => date('Y-m-d'), + 'status_id' => $group_status_id_open, + 'sdd_creditor_id' => $creditor_id, + )); + if (!empty($group['is_error'])) { + // TODO: Error handling + Civi::log()->debug("org.project60.sepa: batching:syncGroups/createGroup ".$group['error_message']); + } + } + else { + try { + $group = \Civi\Api4\SepaTransactionGroup::get(TRUE) + ->addWhere('id', '=', $existing_groups[$collection_date][$financial_type_id ?? 0]) + ->addWhere('status_id', '=', $group_status_id_open) + ->execute() + ->single(); + } + catch (\CRM_Core_Exception $exception) { + // TODO: Error handling + Civi::log()->debug('org.project60.sepa: batching:syncGroups/getGroup ' . $exception->getMessage()); + } + unset($existing_groups[$collection_date][$financial_type_id ?? 0]); + } + + return (int) $group['id']; + } + /** * subroutine to create the group/contribution structure as calculated * @param $calculated_groups array [collection_date] -> array(contributions) as calculated @@ -449,100 +542,122 @@ static function closeEnded() { * @param $partial_groups Is this a partial update? * @param $partial_first Is this the first call in a partial update? */ - protected static function syncGroups($calculated_groups, $existing_groups, $mode, $type, $notice, $creditor_id, $partial_groups=FALSE, $partial_first=FALSE) { + protected static function syncGroups( + $calculated_groups, + $existing_groups, + $mode, + $type, + $notice, + $creditor_id, + $partial_groups=FALSE, + $partial_first=FALSE + ) { $group_status_id_open = (int) CRM_Core_PseudoConstant::getKey('CRM_Batch_BAO_Batch', 'status_id', 'Open'); - foreach ($calculated_groups as $collection_date => $mandates) { + foreach ($calculated_groups as $collection_date => $financial_type_groups) { // check if we need to defer the collection date (e.g. due to bank holidays) self::deferCollectionDate($collection_date, $creditor_id); - if (!isset($existing_groups[$collection_date])) { - // this group does not yet exist -> create + // If not using financial type grouping, flatten to a "0" financial type. + if (!CRM_Sepa_Logic_Settings::getGenericSetting('sdd_financial_type_grouping')) { + $financial_type_groups = [0 => array_merge(...$financial_type_groups)]; + } - // find unused reference - $reference = "TXG-{$creditor_id}-{$mode}-{$collection_date}"; - $counter = 0; - while (self::referenceExists($reference)) { - $counter += 1; - $reference = "TXG-{$creditor_id}-{$mode}-{$collection_date}--".$counter; - } + foreach ($financial_type_groups as $financial_type_id => $mandates) { + $group_id = self::getOrCreateTransactionGroup( + (int) $creditor_id, + $mode, + $collection_date, + 0 === $financial_type_id ? NULL : $financial_type_id, + (int) $notice, + $existing_groups + ); - // call the hook - CRM_Utils_SepaCustomisationHooks::modify_txgroup_reference($reference, $creditor_id, $mode, $collection_date); - - $group = civicrm_api('SepaTransactionGroup', 'create', array( - 'version' => 3, - 'reference' => $reference, - 'type' => $mode, - 'collection_date' => $collection_date, - 'latest_submission_date' => date('Y-m-d', strtotime("-$notice days", strtotime($collection_date))), - 'created_date' => date('Y-m-d'), - 'status_id' => $group_status_id_open, - 'sdd_creditor_id' => $creditor_id, - )); - if (!empty($group['is_error'])) { - // TODO: Error handling - Civi::log()->debug("org.project60.sepa: batching:syncGroups/createGroup ".$group['error_message']); + // now we have the right group. Prepare some parameters... + $entity_ids = []; + foreach ($mandates as $mandate) { + // remark: "mandate_entity_id" in this case means the contribution ID + if (empty($mandate['mandate_entity_id'])) { + // this shouldn't happen + Civi::log() + ->debug("org.project60.sepa: batching:syncGroups mandate with bad mandate_entity_id ignored:" . $mandate['mandate_id']); + } + else { + array_push($entity_ids, $mandate['mandate_entity_id']); + } } - } else { - $group = civicrm_api('SepaTransactionGroup', 'getsingle', array('version' => 3, 'id' => $existing_groups[$collection_date], 'status_id' => $group_status_id_open)); - if (!empty($group['is_error'])) { - // TODO: Error handling - Civi::log()->debug("org.project60.sepa: batching:syncGroups/getGroup ".$group['error_message']); + if (count($entity_ids) <= 0) { + continue; } - unset($existing_groups[$collection_date]); - } - // now we have the right group. Prepare some parameters... - $group_id = $group['id']; - $entity_ids = array(); - foreach ($mandates as $mandate) { - // remark: "mandate_entity_id" in this case means the contribution ID - if (empty($mandate['mandate_entity_id'])) { - // this shouldn't happen - Civi::log()->debug("org.project60.sepa: batching:syncGroups mandate with bad mandate_entity_id ignored:" . $mandate['mandate_id']); - } else { - array_push($entity_ids, $mandate['mandate_entity_id']); + // now, filter out the entity_ids that are are already in a non-open group + // (DO NOT CHANGE CLOSED GROUPS!) + $entity_ids_list = implode(',', $entity_ids); + $already_sent_contributions = CRM_Core_DAO::executeQuery( + << $group_status_id_open; + SQL + ); + while ($already_sent_contributions->fetch()) { + $index = array_search($already_sent_contributions->contribution_id, $entity_ids); + if ($index !== FALSE) { + unset($entity_ids[$index]); + } + } + if (count($entity_ids) <= 0) { + continue; } - } - if (count($entity_ids)<=0) continue; - - // now, filter out the entity_ids that are are already in a non-open group - // (DO NOT CHANGE CLOSED GROUPS!) - $entity_ids_list = implode(',', $entity_ids); - $already_sent_contributions = CRM_Core_DAO::executeQuery(" - SELECT contribution_id - FROM civicrm_sdd_contribution_txgroup - LEFT JOIN civicrm_sdd_txgroup ON civicrm_sdd_contribution_txgroup.txgroup_id = civicrm_sdd_txgroup.id - WHERE contribution_id IN ($entity_ids_list) - AND civicrm_sdd_txgroup.status_id <> $group_status_id_open;"); - while ($already_sent_contributions->fetch()) { - $index = array_search($already_sent_contributions->contribution_id, $entity_ids); - if ($index !== false) unset($entity_ids[$index]); - } - if (count($entity_ids)<=0) continue; - // remove all the unwanted entries from our group - $entity_ids_list = implode(',', $entity_ids); - if (!$partial_groups || $partial_first) { - CRM_Core_DAO::executeQuery("DELETE FROM civicrm_sdd_contribution_txgroup WHERE txgroup_id=$group_id AND contribution_id NOT IN ($entity_ids_list);"); - } + // remove all the unwanted entries from our group + $entity_ids_list = implode(',', $entity_ids); + if (!$partial_groups || $partial_first) { + CRM_Core_DAO::executeQuery( + <<fetch()) { - // remove from entity ids, if in there: - if(($key = array_search($existing->contribution_id, $entity_ids)) !== false) { - unset($entity_ids[$key]); + // now check which ones are already in our group... + $existing = CRM_Core_DAO::executeQuery( + <<fetch()) { + // remove from entity ids, if in there: + if (($key = array_search($existing->contribution_id, $entity_ids)) !== FALSE) { + unset($entity_ids[$key]); + } } - } - // the remaining must be added - foreach ($entity_ids as $entity_id) { - CRM_Core_DAO::executeQuery("INSERT INTO civicrm_sdd_contribution_txgroup (txgroup_id, contribution_id) VALUES ($group_id, $entity_id);"); + // the remaining must be added + foreach ($entity_ids as $entity_id) { + CRM_Core_DAO::executeQuery( + <<$reference, 'version'=>3)); - // this should return an error, if the group exists - return !(isset($query['is_error']) && $query['is_error']); + return \Civi\Api4\SepaTransactionGroup::get(TRUE) + ->selectRowCount() + ->addWhere('reference', '=', $reference) + ->execute() + ->count() === 1; + } + + public static function getTransactionGroupReference( + int $creditorId, + string $mode, + string $collectionDate, + ?int $financialTypeId = NULL + ): string { + $defaultReference = "TXG-{$creditorId}-{$mode}-{$collectionDate}"; + if (isset($financialTypeId)) { + $defaultReference .= "-{$financialTypeId}"; + } + + $counter = 0; + $reference = $defaultReference; + while (self::referenceExists($reference)) { + $counter += 1; + $reference = "{$defaultReference}--".$counter; + } + + // Call the hook. + CRM_Utils_SepaCustomisationHooks::modify_txgroup_reference( + $reference, + $creditorId, + $mode, + $collectionDate, + $financialTypeId + ); + + return $reference; } /** @@ -630,10 +777,12 @@ public static function getCycleDay($rcontribution, $creditor_id) { if (!empty($rcontribution['mandate_first_executed'])) { $date = $rcontribution['mandate_first_executed']; } else { - $mandate = civicrm_api3('SepaMandate', 'getsingle', array( - 'entity_table' => 'civicrm_contribution_recur', - 'entity_id' => $rcontribution['id'], - 'return' => 'status')); + $mandate = \Civi\Api4\SepaMandate::get(TRUE) + ->addSelect('status') + ->addWhere('entity_table', '=', 'civicrm_contribution_recur') + ->addWhere('entity_id', '=', $rcontribution['id']) + ->execute() + ->single(); if ($mandate['status'] == 'RCUR') { // support for RCUR without first contribution diff --git a/CRM/Sepa/Logic/Group.php b/CRM/Sepa/Logic/Group.php index 27e3a14e..c1bc59e4 100644 --- a/CRM/Sepa/Logic/Group.php +++ b/CRM/Sepa/Logic/Group.php @@ -44,10 +44,15 @@ static function close($txgroup_id) { } $status_closed = (int) CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed'); $group_status_id_open = (int) CRM_Core_PseudoConstant::getKey('CRM_Batch_BAO_Batch', 'status_id', 'Open'); - $txgroup = civicrm_api('SepaTransactionGroup', 'getsingle', array('id'=>$txgroup_id, 'version'=>3)); - if (isset($txgroup['is_error']) && $txgroup['is_error']) { + try { + $txgroup = \Civi\Api4\SepaTransactionGroup::get(TRUE) + ->addWhere('id', '=', $txgroup_id) + ->execute() + ->single(); + } + catch (\CRM_Core_Exception $exception) { $lock->release(); - return "Cannot find transaction group ".$txgroup_id; + return 'Cannot find transaction group ' . $txgroup_id; } $collection_date = $txgroup['collection_date']; @@ -169,10 +174,15 @@ static function received($txgroup_id) { return civicrm_api3_create_error("Status 'Pending', 'Completed' or 'In Progress' 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'])) { + try { + $txgroup = \Civi\Api4\SepaTransactionGroup::get(TRUE) + ->addWhere('id', '=', $txgroup_id) + ->execute() + ->single(); + } + catch (\CRM_Core_Exception $exception) { $lock->release(); - return "Cannot find transaction group ".$txgroup_id; + return 'Cannot find transaction group ' . $txgroup_id; } // check status diff --git a/CRM/Sepa/Logic/PaymentInstruments.php b/CRM/Sepa/Logic/PaymentInstruments.php index 290f352f..9e8a1f2c 100644 --- a/CRM/Sepa/Logic/PaymentInstruments.php +++ b/CRM/Sepa/Logic/PaymentInstruments.php @@ -76,7 +76,11 @@ public static function getSDDPaymentInstrumentsForContribution($contribution_id) return null; } else { // get the creditor ID - $mandate = civicrm_api3('SepaMandate', 'getsingle', ['return' => 'creditor_id,type,status', 'id' => $mandate_id]); + $mandate = \Civi\Api4\SepaMandate::get(TRUE) + ->addSelect('creditor_id', 'type', 'status') + ->addWhere('id', '=', $mandate_id) + ->execute() + ->single(); return self::getPaymentInstrumentsForCreditor($mandate['creditor_id'], $mandate['type']); } } diff --git a/CRM/Sepa/Logic/Queue/Close.php b/CRM/Sepa/Logic/Queue/Close.php index 1bec8246..2b8fac60 100644 --- a/CRM/Sepa/Logic/Queue/Close.php +++ b/CRM/Sepa/Logic/Queue/Close.php @@ -48,14 +48,17 @@ public static function launchCloseRunner($txgroup_ids, $target_group_status, $ta $is_received_runner = $target_contribution_status == (int) CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed'); // fetch the groups - $txgroup_query = civicrm_api3('SepaTransactionGroup', 'get', array( - 'id' => ['IN' => $txgroup_ids], - 'option.limit' => 0 - )); + $txgroups = \Civi\Api4\SepaTransactionGroup::get(TRUE) + ->addWhere('id', 'IN', $txgroup_ids) + ->execute(); - $group_status_id_busy = (int) CRM_Core_PseudoConstant::getKey('CRM_Batch_BAO_Batch', 'status_id', 'Data Entry'); + $group_status_id_busy = (int) CRM_Core_PseudoConstant::getKey( + 'CRM_Batch_BAO_Batch', + 'status_id', + 'Data Entry' + ); - foreach ($txgroup_query['values'] as $txgroup) { + foreach ($txgroups as $txgroup) { // first: set group status to busy $queue->createItem(new CRM_Sepa_Logic_Queue_Close('set_group_status', $txgroup, $group_status_id_busy)); diff --git a/CRM/Sepa/Logic/Settings.php b/CRM/Sepa/Logic/Settings.php index f59a815a..b5a6ad84 100644 --- a/CRM/Sepa/Logic/Settings.php +++ b/CRM/Sepa/Logic/Settings.php @@ -260,6 +260,16 @@ public static function isLittleBicExtensionAccessible() { } } + /** + * Whether the "Financial ACLs" Core extension is installed. The extension introduces financial type-specific + * permissions for CRUD actions on contributions, which CiviSEPA is respecting for displaying mandates, etc. + */ + public static function isFinancialaclsInstalled(): bool { + return \CRM_Extension_System::singleton() + ->getManager() + ->getStatus('financialacls') === \CRM_Extension_Manager::STATUS_INSTALLED; + } + /** * Return the ID of the contributions' 'In Progress' status. * diff --git a/CRM/Sepa/Page/CloseGroup.php b/CRM/Sepa/Page/CloseGroup.php index 9122ec0b..28d3abe7 100644 --- a/CRM/Sepa/Page/CloseGroup.php +++ b/CRM/Sepa/Page/CloseGroup.php @@ -21,93 +21,143 @@ * */ -require_once 'CRM/Core/Page.php'; +use CRM_Sepa_ExtensionUtil as E; class CRM_Sepa_Page_CloseGroup extends CRM_Core_Page { function run() { - CRM_Utils_System::setTitle(ts('Close SEPA Group', array('domain' => 'org.project60.sepa'))); - if (isset($_REQUEST['group_id'])) { - if (isset($_REQUEST['status']) && ($_REQUEST['status'] == "missed" || $_REQUEST['status'] == "invalid" || $_REQUEST['status'] == "closed")) { - $this->assign('status', $_REQUEST['status']); - }else{ - $_REQUEST['status'] = ""; + CRM_Utils_System::setTitle(E::ts('Close SEPA Group')); + $group_id = CRM_Utils_Request::retrieve('group_id', 'Integer'); + $status = CRM_Utils_Request::retrieve('status', 'String'); + if (is_int($group_id)) { + if ('missed' === $status || 'invalid' === $status || 'closed' === $status) { + $this->assign('status', $status); + } + else { + $status = ''; + } + $this->assign('txgid', $group_id); + + // LOAD/CREATE THE TXFILE + try { + $group = \Civi\Api4\SepaTransactionGroup::get(TRUE) + ->addWhere('id', '=', $group_id) + ->execute() + ->single(); + + $this->assign('txgroup', $group); + + // check whether this is a group created by a test creditor + $creditor = civicrm_api('SepaCreditor', 'getsingle', [ + 'version' => 3, + 'id' => $group['sdd_creditor_id'], + ]); + if (isset($creditor['is_error']) && $creditor['is_error']) { + CRM_Core_Session::setStatus( + E::ts('Cannot load creditor.') . '
' + . E::ts('Error was: %1', [1 => $creditor['error_message']]), + E::ts('Error'), + 'error' + ); } - - $group_id = (int) $_REQUEST['group_id']; - $this->assign('txgid', $group_id); - - // LOAD/CREATE THE TXFILE - $group = civicrm_api('SepaTransactionGroup', 'getsingle', array('version'=>3, 'id'=>$group_id)); - if (isset($group['is_error']) && $group['is_error']) { - CRM_Core_Session::setStatus("Cannot load group #$group_id.
Error was: ".$group['error_message'], ts('Error', array('domain' => 'org.project60.sepa')), 'error'); - } else { - $this->assign('txgroup', $group); - - // check whether this is a group created by a test creditor - $creditor = civicrm_api('SepaCreditor', 'getsingle', array('version'=>3, 'id'=>$group['sdd_creditor_id'])); - if (isset($creditor['is_error']) && $creditor['is_error']) { - CRM_Core_Session::setStatus("Cannot load creditor.
Error was: ".$creditor['error_message'], ts('Error', array('domain' => 'org.project60.sepa')), 'error'); - }else{ - // check for test group - $isTestGroup = isset($creditor['category']) && ($creditor['category'] == "TEST"); - $this->assign('is_test_group', $isTestGroup); - - // check if this is allowed - $no_draftxml = CRM_Sepa_Logic_Settings::getGenericSetting('sdd_no_draft_xml'); - $this->assign('allow_xml', !$no_draftxml); - - if ($_REQUEST['status'] == "") { - // first adjust group's collection date if requested - if (!empty($_REQUEST['adjust'])) { - $result = CRM_Sepa_BAO_SEPATransactionGroup::adjustCollectionDate($group_id, $_REQUEST['adjust']); - if (is_string($result)) { - // that's an error -> stop here! - die($result); - } else { - // that went well, so result should be the update group data - $group = $result; - } - } - - - // delete old txfile - if (!empty($group['sdd_file_id'])) { - $result = civicrm_api('SepaSddFile', 'delete', array('id'=>$group['sdd_file_id'], 'version'=>3)); - if (isset($result['is_error']) && $result['is_error']) { - CRM_Core_Session::setStatus("Cannot delete file #".$group['sdd_file_id'].".
Error was: ".$result['error_message'], ts('Error', array('domain' => 'org.project60.sepa')), 'error'); - } - } - - $this->createDownloadLink($group_id); + else { + // check for test group + $isTestGroup = isset($creditor['category']) && ('TEST' === $creditor['category']); + $this->assign('is_test_group', $isTestGroup); + + // check if this is allowed + $no_draftxml = CRM_Sepa_Logic_Settings::getGenericSetting('sdd_no_draft_xml'); + $this->assign('allow_xml', !$no_draftxml); + + if ('' === $status) { + // first adjust group's collection date if requested + $adjust = CRM_Utils_Request::retrieve('adjust', 'String'); + if (!empty($adjust)) { + $result = CRM_Sepa_BAO_SEPATransactionGroup::adjustCollectionDate($group_id, $adjust); + if (is_string($result)) { + // that's an error -> stop here! + // TODO: Do not use die(). + die($result); } - - if ($_REQUEST['status'] == "closed" && !$isTestGroup) { - // CLOSE THE GROUP: - $async_batch = CRM_Sepa_Logic_Settings::getGenericSetting('sdd_async_batching'); - if ($async_batch) { - // call the closing runner - $skip_closed = CRM_Sepa_Logic_Settings::getGenericSetting('sdd_skip_closed'); - if ($skip_closed) { - $target_contribution_status = (int) CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed'); - $target_group_status = (int) CRM_Core_PseudoConstant::getKey('CRM_Batch_BAO_Batch', 'status_id', 'Received'); - } else { - $target_contribution_status = CRM_Sepa_Logic_Settings::contributionInProgressStatusId(); - $target_group_status = (int) CRM_Core_PseudoConstant::getKey('CRM_Batch_BAO_Batch', 'status_id', 'Closed'); - } - // this call doesn't return (redirect to runner) - CRM_Sepa_Logic_Queue_Close::launchCloseRunner(array($group_id), $target_group_status, $target_contribution_status); - } - - $result = civicrm_api('SepaAlternativeBatching', 'close', array('version'=>3, 'txgroup_id'=>$group_id)); - if ($result['is_error']) { - CRM_Core_Session::setStatus("Cannot close group #$group_id.
Error was: ".$result['error_message'], ts('Error', array('domain' => 'org.project60.sepa')), 'error'); - } - $this->createDownloadLink($group_id); + else { + // that went well, so result should be the update group data + $group = $result; + } + } + + // delete old txfile + if (!empty($group['sdd_file_id'])) { + $result = civicrm_api('SepaSddFile', 'delete', [ + 'id' => $group['sdd_file_id'], + 'version' => 3, + ]); + if (isset($result['is_error']) && $result['is_error']) { + CRM_Core_Session::setStatus( + E::ts('Cannot delete file #%1', [1 => $group['sdd_file_id']]) . '
' + . E::ts('Error was: %1', [1 => $result['error_message']]), + E::ts('Error'), + 'error' + ); } + } + + $this->createDownloadLink($group_id); } + if ('closed' === $status && !$isTestGroup) { + // CLOSE THE GROUP: + $async_batch = CRM_Sepa_Logic_Settings::getGenericSetting('sdd_async_batching'); + if ($async_batch) { + // call the closing runner + $skip_closed = CRM_Sepa_Logic_Settings::getGenericSetting('sdd_skip_closed'); + if ($skip_closed) { + $target_contribution_status = (int) CRM_Core_PseudoConstant::getKey( + 'CRM_Contribute_BAO_Contribution', + 'contribution_status_id', + 'Completed' + ); + $target_group_status = (int) CRM_Core_PseudoConstant::getKey( + 'CRM_Batch_BAO_Batch', + 'status_id', + 'Received' + ); + } + else { + $target_contribution_status = CRM_Sepa_Logic_Settings::contributionInProgressStatusId(); + $target_group_status = (int) CRM_Core_PseudoConstant::getKey( + 'CRM_Batch_BAO_Batch', + 'status_id', + 'Closed' + ); + } + // this call doesn't return (redirect to runner) + CRM_Sepa_Logic_Queue_Close::launchCloseRunner([$group_id], $target_group_status, $target_contribution_status); + } + + $result = civicrm_api('SepaAlternativeBatching', 'close', [ + 'version' => 3, + 'txgroup_id' => $group_id, + ]); + if ($result['is_error']) { + CRM_Core_Session::setStatus( + E::ts('Cannot close group #%1', [1 => $group_id]) . '
' + . E::ts('Error was: %1', [1 => $result['error_message']]), + E::ts('Error'), + 'error' + ); + } + $this->createDownloadLink($group_id); + } } + } + catch (\CRM_Core_Exception $exception) { + CRM_Core_Session::setStatus( + E::ts('Cannot load group #%1', [1 => $group_id]) . '
' + . E::ts('Error was: %1', [1 => $group['error_message']]), + E::ts('Error'), + 'error' + ); + } } parent::run(); @@ -117,13 +167,24 @@ function run() { * generate an XML download link and assign to the template */ protected function createDownloadLink($group_id) { - $xmlfile = civicrm_api('SepaAlternativeBatching', 'createxml', array('txgroup_id'=>$group_id, 'override'=>True, 'version'=>3)); + $xmlfile = civicrm_api( + 'SepaAlternativeBatching', + 'createxml', + ['txgroup_id' => $group_id, 'override' => TRUE, 'version' => 3] + ); if (isset($xmlfile['is_error']) && $xmlfile['is_error']) { - CRM_Core_Session::setStatus("Cannot load for group #".$group_id.".
Error was: ".$xmlfile['error_message'], ts('Error', array('domain' => 'org.project60.sepa')), 'error'); - }else{ + CRM_Core_Session::setStatus( + E::ts('Cannot load for group #%1', [1 => $group_id]) . '
' + . E::ts('Error was: %1', [1 => $xmlfile['error_message']]), + E::ts('Error'), + 'error' + ); + } + else { $file_id = $xmlfile['id']; $this->assign('file_link', CRM_Utils_System::url('civicrm/sepa/xml', "id=$file_id")); $this->assign('file_name', $xmlfile['filename']); } } + } diff --git a/CRM/Sepa/Page/CreateMandate.php b/CRM/Sepa/Page/CreateMandate.php index 71ea6ad9..4194c5dd 100644 --- a/CRM/Sepa/Page/CreateMandate.php +++ b/CRM/Sepa/Page/CreateMandate.php @@ -28,6 +28,8 @@ require_once 'CRM/Core/Page.php'; require_once 'packages/php-iban-1.4.0/php-iban.php'; +use CRM_Sepa_ExtensionUtil as E; + class CRM_Sepa_Page_CreateMandate extends CRM_Core_Page { function run() { @@ -327,9 +329,18 @@ function prepareCreateForm($contact_id) { * Will prepare the form by cloning the data from the given mandate */ function prepareClonedData($mandate_id) { - $mandate = civicrm_api('SepaMandate', 'getsingle', array('id'=>$mandate_id, 'version'=>3)); - if (isset($mandate['is_error']) && $mandate['is_error']) { - CRM_Core_Session::setStatus(sprintf(ts("Couldn't load mandate #%s", array('domain' => 'org.project60.sepa')), $mandate_id), ts('Error', array('domain' => 'org.project60.sepa')), 'error'); + try { + $mandate = \Civi\Api4\SepaMandate::get(TRUE) + ->addWhere('id', '=', $mandate) + ->execute() + ->single(); + } + catch (\CRM_Core_Exception $exception) { + CRM_Core_Session::setStatus( + E::ts("Couldn't load mandate #%1", [1 => $mandate_id]), + E::ts('Error'), + 'error' + ); return; } @@ -440,15 +451,20 @@ function validateParameters() { } // check reference - if (!empty($_REQUEST['reference'])) { + $reference = CRM_Utils_Request::retrieve('reference', 'String'); + if (!empty($reference)) { // check if it is formally correct - if (!preg_match("/^[A-Z0-9\\-]{4,35}$/", $_REQUEST['reference'])) { + if (!preg_match("/^[A-Z0-9\\-]{4,35}$/", $reference)) { $errors['reference'] = ts("Reference has to be an upper case alphanumeric string between 4 and 35 characters long.", array('domain' => 'org.project60.sepa')); } else { // check if the reference is taken - $count = civicrm_api3('SepaMandate', 'getcount', array("reference" => $_REQUEST['reference'])); + $count = \Civi\Api4\SepaMandate::get(TRUE) + ->selectRowCount() + ->addWhere('reference', '=', $reference) + ->execute() + ->countMatched(); if ($count > 0) { - $errors['reference'] = ts("This reference is already in use.", array('domain' => 'org.project60.sepa')); + $errors['reference'] = E::ts('This reference is already in use.'); } } } diff --git a/CRM/Sepa/Page/DashBoard.php b/CRM/Sepa/Page/DashBoard.php index 84a3b073..5eb93c7b 100644 --- a/CRM/Sepa/Page/DashBoard.php +++ b/CRM/Sepa/Page/DashBoard.php @@ -21,7 +21,7 @@ * */ -require_once 'CRM/Core/Page.php'; +use CRM_Sepa_ExtensionUtil as E; class CRM_Sepa_Page_DashBoard extends CRM_Core_Page { @@ -31,13 +31,8 @@ class CRM_Sepa_Page_DashBoard extends CRM_Core_Page { function run() { CRM_Utils_System::setTitle(ts('CiviSEPA Dashboard', array('domain' => 'org.project60.sepa'))); // get requested group status - if (isset($_REQUEST['status'])) { - if ($_REQUEST['status'] != 'open' && $_REQUEST['status'] != 'closed') { - $status = 'open'; - } else { - $status = $_REQUEST['status']; - } - } else { + $status = CRM_Utils_Request::retrieve('status', 'String'); + if ('open' !== $status && 'closed' !== $status) { $status = 'open'; } @@ -52,6 +47,12 @@ function run() { // check permissions $this->assign('can_delete', CRM_Core_Permission::check('delete sepa groups')); $this->assign('can_batch', CRM_Core_Permission::check('batch sepa groups')); + $this->assign( + 'financialacls', + \CRM_Extension_System::singleton() + ->getManager() + ->getStatus('financialacls') === \CRM_Extension_Manager::STATUS_INSTALLED + ); if (isset($_REQUEST['update'])) { $this->callBatcher($_REQUEST['update']); @@ -88,38 +89,81 @@ function run() { $this->assign('closed_status_id', CRM_Core_PseudoConstant::getKey('CRM_Batch_BAO_Batch', 'status_id', 'Closed')); // now read the details - $result = civicrm_api("SepaTransactionGroup", "getdetail", array( - "version" => 3, - "sequential" => 1, - "status_ids" => implode(',', $status_list[$status]), - "order_by" => (($status=='open')?'latest_submission_date':'file.created_date'), - )); - if (isset($result['is_error']) && $result['is_error']) { - CRM_Core_Session::setStatus(sprintf(ts("Couldn't read transaction groups. Error was: '%s'", array('domain' => 'org.project60.sepa')), $result['error_message']), ts('Error', array('domain' => 'org.project60.sepa')), 'error'); - } else { + try { + // API4 SepaTransactionGroup.get checks Financial ACLs for corresponding contributions. + $sepaTransactionGroups = \Civi\Api4\SepaTransactionGroup::get(TRUE) + ->selectRowCount() + ->addSelect( + 'id', + 'reference', + 'sdd_file_id', + 'type', + 'collection_date', + 'latest_submission_date', + 'status_id', + 'sdd_creditor_id', + 'sepa_sdd_file.created_date', + 'SUM(contribution.total_amount) AS total', + 'COUNT(*) AS nb_contrib', + 'contribution.currency', + 'sepa_sdd_file.reference' + ) + ->addJoin( + 'Contribution AS contribution', + 'LEFT', + 'SepaContributionGroup' + ) + ->addJoin( + 'SepaSddFile AS sepa_sdd_file', + 'LEFT', + ['sdd_file_id', '=', 'sepa_sdd_file.id'] + ) + ->addWhere('status_id', 'IN', $status_list[$status]) + ->addOrderBy('open' === $status ? 'latest_submission_date' : 'sepa_sdd_file.created_date') + ->addGroupBy('id') + ->addGroupBy('reference') + ->addGroupBy('sdd_file_id') + ->addGroupBy('type') + ->addGroupBy('collection_date') + ->addGroupBy('latest_submission_date') + ->addGroupBy('status_id') + ->addGroupBy('sdd_creditor_id') + ->addGroupBy('sepa_sdd_file.created_date') + ->addGroupBy('contribution.currency') + ->addGroupBy('sepa_sdd_file.reference') + ->execute(); $groups = []; $now = date('Y-m-d'); - foreach ($result["values"] as $id => $group) { - // 'beautify' + foreach ($sepaTransactionGroups as $group) { $group['latest_submission_date'] = date('Y-m-d', strtotime($group['latest_submission_date'])); $group['collection_date'] = date('Y-m-d', strtotime($group['collection_date'])); $group['collection_date_in_future'] = ($group['collection_date'] > $now) ? 1 : 0; $group['status'] = $status_2_title[$group['status_id']]; + $group['currency'] = $group['contribution.currency']; + $group['file_id'] = $group['sdd_file_id']; + $group['file_created_date'] = $group['sepa_sdd_file.created_date']; + $group['creditor_id'] = $group['sdd_creditor_id']; + $group['file'] = $group['sepa_sdd_file.reference']; $group['file'] = $this->getFormatFilename($group); $group['status_label'] = $status2label[$group['status_id']]; - $remaining_days = (strtotime($group['latest_submission_date']) - strtotime("now")) / (60*60*24); - if ($group['status']=='closed') { + $remaining_days = (strtotime($group['latest_submission_date']) - strtotime("now")) / (60 * 60 * 24); + if ('closed' === $group['status']) { $group['submit'] = 'closed'; - } elseif ($group['type'] == 'OOFF') { + } + elseif ('OOFF' === $group['type']) { $group['submit'] = 'soon'; - } else { + } + else { if ($remaining_days <= -1) { $group['submit'] = 'missed'; - } elseif ($remaining_days <= 1) { + } + elseif ($remaining_days <= 1) { $group['submit'] = 'urgently'; - } elseif ($remaining_days <= 6) { + } + elseif ($remaining_days <= 6) { $group['submit'] = 'soon'; - } else { + } + else { $group['submit'] = 'later'; } } @@ -127,18 +171,24 @@ function run() { $group['transaction_message'] = CRM_Sepa_BAO_SEPATransactionGroup::getCustomGroupTransactionMessage($group['id']); $group['transaction_note'] = CRM_Sepa_BAO_SEPATransactionGroup::getNote($group['id']); - array_push($groups, $group); + $groups[] = $group; } - $this->assign("groups", $groups); + $this->assign('groups', $groups); + } + catch (CRM_Core_Exception $exception) { + CRM_Core_Session::setStatus( + E::ts( + "Couldn't read transaction groups. Error was: %1", + [1 => $exception->getMessage()] + ), + E::ts('Error'), + 'error' + ); } parent::run(); } - function getTemplateFileName() { - return "CRM/Sepa/Page/DashBoard.tpl"; - } - /** * call the batching API */ diff --git a/CRM/Sepa/Page/DeleteGroup.php b/CRM/Sepa/Page/DeleteGroup.php index ef16f7eb..58f55250 100644 --- a/CRM/Sepa/Page/DeleteGroup.php +++ b/CRM/Sepa/Page/DeleteGroup.php @@ -26,76 +26,98 @@ class CRM_Sepa_Page_DeleteGroup extends CRM_Core_Page { function run() { - CRM_Utils_System::setTitle(ts('Delete SEPA Group', array('domain' => 'org.project60.sepa'))); + CRM_Utils_System::setTitle(ts('Delete SEPA Group', ['domain' => 'org.project60.sepa'])); if (empty($_REQUEST['group_id'])) { - $this->assign('status', 'error'); - - } else { - $group_id = (int) $_REQUEST['group_id']; - $this->assign('txgid', $group_id); - $txgroup = civicrm_api('SepaTransactionGroup', 'getsingle', array('id'=>$group_id, 'version'=>3)); - if (empty($txgroup['is_error'])) { - $txgroup['status_label'] = CRM_Core_PseudoConstant::getLabel('CRM_Batch_BAO_Batch', 'status_id', $txgroup['status_id']); - $txgroup['status_name'] = CRM_Core_PseudoConstant::getName('CRM_Batch_BAO_Batch', 'status_id', $txgroup['status_id']); - $this->assign('txgroup', $txgroup); - } else { - $_REQUEST['confirmed'] = 'error'; // skip the parts below - } - - if (empty($_REQUEST['confirmed'])) { - // gather information to display - $PENDING = (int) CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Pending'); - $INPROGRESS = CRM_Sepa_Logic_Settings::contributionInProgressStatusId(); - - $stats = array('busy' => 0, 'open' => 0, 'other' => 0, 'total' => 0); - $status2contributions = $this->contributionStats($group_id); - foreach ($status2contributions as $contribution_status_id => $contributions) { - foreach ($contributions as $contribution_id) { - $stats['total'] += 1; - if ($contribution_status_id==$PENDING) { - $stats['open'] += 1; - } elseif ($contribution_status_id==$INPROGRESS) { - $stats['busy'] += 1; - } else { - $stats['other'] += 1; - } - } - } - $this->assign('stats', $stats); - $this->assign('status', 'unconfirmed'); - $this->assign('submit_url', CRM_Utils_System::url('civicrm/sepa/deletegroup')); + $this->assign('status', 'error'); + } + else { + $group_id = (int) $_REQUEST['group_id']; + $this->assign('txgid', $group_id); + try { + $txgroup = \Civi\Api4\SepaTransactionGroup::get(TRUE) + ->addWhere('id', '=', $group_id) + ->execute() + ->single(); - } elseif ($_REQUEST['confirmed']=='yes') { - // delete the group - $this->assign('status', 'done'); - $delete_contributions_mode = $_REQUEST['delete_contents']; - $deleted_ok = array(); - $deleted_error = array(); - $result = CRM_Sepa_BAO_SEPATransactionGroup::deleteGroup($group_id, $delete_contributions_mode); - if (is_string($result)) { - // a very basic error happened - $this->assign('error', $result); - } else { - // do some stats on the result - $deleted_total = count($result); - foreach ($result as $contribution_id => $message) { - if ($message=='ok') { - array_push($deleted_ok, $contribution_id); - } else { - array_push($deleted_error, $contribution_id); - } - } - $this->assign('deleted_result', $result); - $this->assign('deleted_ok', $deleted_ok); - $this->assign('deleted_error', $deleted_error); - } + $txgroup['status_label'] = CRM_Core_PseudoConstant::getLabel( + 'CRM_Batch_BAO_Batch', + 'status_id', + $txgroup['status_id'] + ); + $txgroup['status_name'] = CRM_Core_PseudoConstant::getName( + 'CRM_Batch_BAO_Batch', + 'status_id', + $txgroup['status_id'] + ); + $this->assign('txgroup', $txgroup); + } + catch (\CRM_Core_Exception $exception) { + // skip the parts below + $_REQUEST['confirmed'] = 'error'; + } - } elseif ($_REQUEST['confirmed']=='error') { - $this->assign('status', 'error'); + if (empty($_REQUEST['confirmed'])) { + // gather information to display + $PENDING = (int) CRM_Core_PseudoConstant::getKey( + 'CRM_Contribute_BAO_Contribution', + 'contribution_status_id', + 'Pending' + ); + $INPROGRESS = CRM_Sepa_Logic_Settings::contributionInProgressStatusId(); - } else { - CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/sepa')); + $stats = ['busy' => 0, 'open' => 0, 'other' => 0, 'total' => 0]; + $status2contributions = $this->contributionStats($group_id); + foreach ($status2contributions as $contribution_status_id => $contributions) { + foreach ($contributions as $contribution_id) { + $stats['total'] += 1; + if ($contribution_status_id == $PENDING) { + $stats['open'] += 1; + } + elseif ($contribution_status_id == $INPROGRESS) { + $stats['busy'] += 1; + } + else { + $stats['other'] += 1; + } + } + } + $this->assign('stats', $stats); + $this->assign('status', 'unconfirmed'); + $this->assign('submit_url', CRM_Utils_System::url('civicrm/sepa/deletegroup')); + } + elseif ($_REQUEST['confirmed'] == 'yes') { + // delete the group + $this->assign('status', 'done'); + $delete_contributions_mode = $_REQUEST['delete_contents']; + $deleted_ok = []; + $deleted_error = []; + $result = CRM_Sepa_BAO_SEPATransactionGroup::deleteGroup($group_id, $delete_contributions_mode); + if (is_string($result)) { + // a very basic error happened + $this->assign('error', $result); + } + else { + // do some stats on the result + $deleted_total = count($result); + foreach ($result as $contribution_id => $message) { + if ($message == 'ok') { + array_push($deleted_ok, $contribution_id); + } + else { + array_push($deleted_error, $contribution_id); + } + } + $this->assign('deleted_result', $result); + $this->assign('deleted_ok', $deleted_ok); + $this->assign('deleted_error', $deleted_error); } + } + elseif ($_REQUEST['confirmed'] == 'error') { + $this->assign('status', 'error'); + } + else { + CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/sepa')); + } } parent::run(); diff --git a/CRM/Sepa/Page/EditMandate.php b/CRM/Sepa/Page/EditMandate.php index bd6547e6..e6df1acf 100644 --- a/CRM/Sepa/Page/EditMandate.php +++ b/CRM/Sepa/Page/EditMandate.php @@ -24,8 +24,6 @@ * */ -require_once 'CRM/Core/Page.php'; - use CRM_Sepa_ExtensionUtil as E; class CRM_Sepa_Page_EditMandate extends CRM_Core_Page { @@ -65,10 +63,17 @@ function run() { } // first, load the mandate - $mandate = civicrm_api("SepaMandate", "getsingle", array('id'=>$mandate_id, 'version'=>3)); - if (isset($mandate['is_error']) && $mandate['is_error']) { - CRM_Core_Session::setStatus(sprintf(ts("Cannot read mandate [%s]. Error was: '%s'", array('domain' => 'org.project60.sepa')), $mandate_id, $mandate['error_message']), ts('Error', array('domain' => 'org.project60.sepa')), 'error'); - die(sprintf(ts("Cannot find mandate [%s].", array('domain' => 'org.project60.sepa')), $mandate_id)); + try { + // API4 SepaMandate.get checks Financial ACLs for corresponding (recurring) contribution. + $mandate = \Civi\Api4\SepaMandate::get(TRUE) + ->addWhere('id', '=', $mandate_id) + ->execute() + ->single(); + } + catch (\CRM_Core_Exception $exception) { + CRM_Core_Error::statusBounce( + E::ts("Cannot read mandate [%1]. Error was: '%2'", [1 => $mandate_id, 2 => $exception->getMessage()]) + ); } // load the contribution diff --git a/CRM/Sepa/Page/ListGroup.php b/CRM/Sepa/Page/ListGroup.php index 862bd82f..d7a585f3 100644 --- a/CRM/Sepa/Page/ListGroup.php +++ b/CRM/Sepa/Page/ListGroup.php @@ -14,119 +14,110 @@ | written permission from the original author(s). | +--------------------------------------------------------*/ +use CRM_Sepa_ExtensionUtil as E; +use Civi\Api4\SepaTransactionGroup; +use Civi\Api4\Contribution; + /** * back office sepa group content viewer * * @package CiviCRM_SEPA * */ - - -require_once 'CRM/Core/Page.php'; - class CRM_Sepa_Page_ListGroup extends CRM_Core_Page { function run() { - CRM_Utils_System::setTitle(ts('SEPA Group Contributions', array('domain' => 'org.project60.sepa'))); - if (isset($_REQUEST['group_id'])) { - // get some values - $group_id = (int) $_REQUEST['group_id']; - $financial_types = CRM_Contribute_PseudoConstant::financialType(); - - // load the group - $txgroup = civicrm_api('SepaTransactionGroup', 'getsingle', array('id'=>$group_id, 'version'=>3)); - if (isset($txgroup['is_error']) && $txgroup['is_error']) { - CRM_Core_Session::setStatus(sprintf(ts("Cannot read SEPA transaction group [%s]. Error was: '%s'", array('domain' => 'org.project60.sepa')), $group_id, $txgroup['error_message']), ts("Error", array('domain' => 'org.project60.sepa')), "error"); - } - - // get contribution status option group ID - $status_option_group_id = 99999; - $option_group = civicrm_api3('OptionGroup', 'get', array( - 'name' => 'contribution_status', - 'return' => 'id')); - if (!empty($option_group['id'])) { - $status_option_group_id = $option_group['id']; - } - - // load the group's contributions - $sql = " - SELECT - civicrm_sdd_txgroup.reference AS reference, - civicrm_contact.display_name AS contact_display_name, - civicrm_contact.contact_type AS contact_contact_type, - civicrm_contact.id AS contact_id, - civicrm_contribution.id AS contribution_id, - civicrm_contribution.total_amount AS contribution_amount, - civicrm_contribution.currency AS contribution_currency, - civicrm_contribution.financial_type_id AS contribution_financial_type_id, - civicrm_campaign.title AS contribution_campaign, - civicrm_option_value.label AS contribution_status - FROM - civicrm_sdd_txgroup - LEFT JOIN - civicrm_sdd_contribution_txgroup ON civicrm_sdd_txgroup.id = civicrm_sdd_contribution_txgroup.txgroup_id - LEFT JOIN - civicrm_contribution ON civicrm_contribution.id = civicrm_sdd_contribution_txgroup.contribution_id - LEFT JOIN - civicrm_contact ON civicrm_contact.id = civicrm_contribution.contact_id - LEFT JOIN - civicrm_campaign ON civicrm_campaign.id = civicrm_contribution.campaign_id - LEFT JOIN - civicrm_option_value ON civicrm_option_value.value = civicrm_contribution.contribution_status_id AND civicrm_option_value.option_group_id = {$status_option_group_id} - WHERE - civicrm_sdd_txgroup.id = $group_id;"; - - $total_amount = 0.0; - $total_count = 0; - $total_campaigns = array(); - $total_types = array(); - $total_contacts = array(); - $status_stats = array(); - $contact_base_link = CRM_Utils_System::url('civicrm/contact/view', '&reset=1&cid='); - $contribution_base_link = CRM_Utils_System::url('civicrm/contact/view/contribution', '&reset=1&id=_cid_&cid=_id_&action=view'); - $currency = NULL; - - $contributions = array(); - $result = CRM_Core_DAO::executeQuery($sql); - while ($result->fetch()) { - $currency = $result->contribution_currency; - $contributions[$total_count] = array( - 'contact_display_name' => $result->contact_display_name, - 'contact_type' => $result->contact_contact_type, - 'contact_id' => $result->contact_id, - 'contact_link' => $contact_base_link.$result->contact_id, - 'contribution_link' => str_replace('_id_', $result->contact_id, str_replace('_cid_', $result->contribution_id, $contribution_base_link)), - 'contribution_id' => $result->contribution_id, - 'contribution_status' => $result->contribution_status, - 'contribution_amount' => $result->contribution_amount, - 'contribution_amount_str' => CRM_Utils_Money::format($result->contribution_amount, $result->contribution_currency), - 'financial_type' => $financial_types[$result->contribution_financial_type_id], - 'campaign' => $result->contribution_campaign, - ); + CRM_Utils_System::setTitle(E::ts('SEPA Group Contributions')); + try { + $groupId = CRM_Utils_Request::retrieve('group_id', 'Integer', NULL, TRUE); + $txGroup = SepaTransactionGroup::get(TRUE) + ->selectRowCount() + ->addSelect( + 'id', + 'reference', + 'status_id', + 'COUNT(DISTINCT contribution.id) AS total_count', + 'SUM(contribution.total_amount) AS total_amount', + 'COUNT(DISTINCT contribution.campaign_id) AS different_campaigns', + 'COUNT(DISTINCT contribution.financial_type_id) AS different_types', + 'COUNT(DISTINCT contribution.contact_id) AS different_contacts' + ) + ->addJoin('Contribution AS contribution', 'INNER', 'SepaContributionGroup') + ->addWhere('id', '=', $groupId) + ->addGroupBy('id') + ->execute() + ->single(); + } + catch (CRM_Core_Exception $exception) { + CRM_Core_Error::statusBounce( + E::ts( + 'Cannot read SEPA transaction group [%1]. Error was: %2', + [ + 1 => $groupId, + 2 => $exception->getMessage(), + ] + ), + CRM_Utils_System::url('civicrm/sepa') + ); + } - $total_count += 1; - $total_amount += $result->contribution_amount; - $total_types[$result->contribution_financial_type_id] = 1; - $total_contacts[$result->contact_id] = 1; - $total_campaigns[$result->contribution_campaign] = 1; - $reference = $result->reference; - $status_stats[$result->contribution_status] = 1 + CRM_Utils_Array::value($result->contribution_status, $status_stats, 0); - } + $result = Contribution::get() + ->selectRowCount() + ->addSelect( + 'contact_id.display_name', + 'contact_id.contact_type', + 'contact_id.id', + 'id', + 'total_amount', + 'currency', + 'financial_type_id', + 'financial_type_id:label', + 'campaign_id.title', + 'contribution_status_id:label' + ) + ->addJoin('SepaTransactionGroup AS sepa_transaction_group', 'INNER', 'SepaContributionGroup') + ->addWhere('sepa_transaction_group.id', '=', $groupId) + ->execute(); + $statusStats = []; + $contributions = []; + foreach ($result as $contribution) { + $contributions[] = [ + 'contact_display_name' => $contribution['contact_id.display_name'], + 'contact_type' => $contribution['contact_id.contact_type'], + 'contact_id' => $contribution['contact_id.id'], + 'contact_link' => CRM_Utils_System::url( + 'civicrm/contact/view', + '&reset=1&cid=' . $contribution['contact_id.id'] + ), + 'contribution_link' => CRM_Utils_System::url( + 'civicrm/contact/view/contribution', + '&reset=1&id=' . $contribution['id'] . '&cid=' . $contribution['contact_id.id'] . '&action=view' + ), + 'contribution_id' => $contribution['id'], + 'contribution_status' => $contribution['contribution_status_id:label'], + 'contribution_amount' => $contribution['total_amount'], + 'contribution_amount_str' => CRM_Utils_Money::format($contribution['total_amount'], $contribution['currency']), + 'financial_type_id' => $contribution['financial_type_id'], + 'financial_type' => $contribution['financial_type_id:label'], + 'campaign' => $contribution['campaign_id.title'], + ]; + $statusStats[$contribution['contribution_status_id:label']] = + 1 + ($statusStats[$contribution['contribution_status_id:label']] ?? 0); } - $this->assign("txgroup", $txgroup); - $this->assign("reference", $reference); - $this->assign("group_id", $group_id); - $this->assign("total_count", $total_count); - $this->assign("total_amount", $total_amount); - $this->assign("total_amount_str", CRM_Utils_Money::format($total_amount, $currency)); - $this->assign("contributions", $contributions); - $this->assign("different_campaigns", count($total_campaigns)); - $this->assign("different_types", count($total_types)); - $this->assign("different_contacts", count($total_contacts)); - $this->assign("status_stats", $status_stats); + $this->assign('txgroup', $txGroup); + $this->assign('reference', $txGroup['reference']); + $this->assign('group_id', $groupId); + $this->assign('total_count', $txGroup['total_count']); + $this->assign('total_amount', $txGroup['total_amount']); + $this->assign('total_amount_str', CRM_Utils_Money::format($txGroup['total_amount'], $result->first()['currency'])); + $this->assign('contributions', $contributions); + $this->assign('different_campaigns', $txGroup['different_campaigns']); + $this->assign('different_types', $txGroup['different_types']); + $this->assign('different_contacts', $txGroup['different_contacts']); + $this->assign('status_stats', $statusStats); parent::run(); } -} \ No newline at end of file +} diff --git a/CRM/Sepa/Page/MandateTab.php b/CRM/Sepa/Page/MandateTab.php index 81a89345..25d1c6e8 100644 --- a/CRM/Sepa/Page/MandateTab.php +++ b/CRM/Sepa/Page/MandateTab.php @@ -15,6 +15,8 @@ +--------------------------------------------------------*/ use CRM_Sepa_ExtensionUtil as E; +use Civi\Api4\SepaMandate; +use Civi\Api4\Contribution; class CRM_Sepa_Page_MandateTab extends CRM_Core_Page { @@ -23,156 +25,189 @@ class CRM_Sepa_Page_MandateTab extends CRM_Core_Page { */ public function run() { CRM_Utils_System::setTitle(E::ts('SEPA Mandates')); - $contact_id = CRM_Utils_Request::retrieve('cid', 'Integer'); + $contactId = CRM_Utils_Request::retrieve('cid', 'Integer'); $this->assign('date_format', '%Y-%m-%d'); - $this->assign('contact_id', $contact_id); - - - // ============================== - // == OOFF == - // ============================== - $ooff_list = array(); - $ooff_query = " - SELECT - civicrm_sdd_mandate.id AS mandate_id, - civicrm_contribution.id AS contribution_id, - civicrm_contribution.receive_date AS receive_date, - civicrm_sdd_mandate.status AS status, - civicrm_sdd_mandate.reference AS reference, - civicrm_financial_type.name AS financial_type, - civicrm_campaign.title AS campaign, - civicrm_contribution.total_amount AS total_amount, - civicrm_contribution.currency AS currency, - civicrm_contribution.cancel_reason AS cancel_reason, - IF(civicrm_sdd_mandate.status IN ('OOFF'), 'sepa-active', 'sepa-inactive') - AS class - FROM civicrm_sdd_mandate - LEFT JOIN civicrm_contribution ON civicrm_contribution.id = civicrm_sdd_mandate.entity_id - LEFT JOIN civicrm_financial_type ON civicrm_financial_type.id = civicrm_contribution.financial_type_id - LEFT JOIN civicrm_campaign ON civicrm_campaign.id = civicrm_contribution.campaign_id - WHERE civicrm_sdd_mandate.contact_id = %1 - AND civicrm_sdd_mandate.type = 'OOFF' - AND civicrm_sdd_mandate.entity_table = 'civicrm_contribution'"; - - $ooff_mandates = CRM_Core_DAO::executeQuery($ooff_query, - array( 1 => array($contact_id, 'Integer'))); - - while ($ooff_mandates->fetch()) { - $ooff = array( - 'receive_date' => $ooff_mandates->receive_date, - 'status_raw' => $ooff_mandates->status, - 'status' => CRM_Sepa_Logic_Status::translateMandateStatus($ooff_mandates->status, TRUE), - 'reference' => $ooff_mandates->reference, - 'financial_type' => $ooff_mandates->financial_type, - 'campaign' => $ooff_mandates->campaign, - 'total_amount' => $ooff_mandates->total_amount, - 'currency' => $ooff_mandates->currency, - 'cancel_reason' => $ooff_mandates->cancel_reason, - 'class' => $ooff_mandates->class, - ); - - // add links - $ooff['view_link'] = CRM_Utils_System::url('civicrm/contact/view/contribution', "reset=1&id={$ooff_mandates->contribution_id}&cid={$contact_id}&action=view&context=contribution"); - if (CRM_Core_Permission::check('edit sepa mandates')) { - $ooff['edit_link'] = CRM_Utils_System::url('civicrm/sepa/xmandate', "mid={$ooff_mandates->mandate_id}"); - } + $this->assign('contact_id', $contactId); + $this->assign( + 'financialacls', + \CRM_Extension_System::singleton() + ->getManager() + ->getStatus('financialacls') === \CRM_Extension_Manager::STATUS_INSTALLED + ); + $this->assign( + 'permissions', + [ + 'create' => CRM_Core_Permission::check('create sepa mandates'), + 'view' => CRM_Core_Permission::check('view sepa mandates'), + 'edit' => CRM_Core_Permission::check('edit sepa mandates'), + 'delete' => CRM_Core_Permission::check('delete sepa mandates'), + ] + ); - $ooff_list[] = $ooff; + // Retrieve OOFF mandates. + $ooffList = []; + $ooffMandates = SepaMandate::get(TRUE) + ->addSelect( + 'id', + 'contribution.id', + 'contribution.receive_date', + 'status', + 'reference', + 'contribution.financial_type_id:name', + 'campaign.title', + 'contribution.total_amount', + 'contribution.currency', + 'contribution.cancel_reason' + ) + ->addJoin( + 'Contribution AS contribution', + 'INNER', + ['entity_table', '=', '"civicrm_contribution"'], + ['entity_id', '=', 'contribution.id'] + ) + ->addJoin( + 'Campaign AS campaign', + 'LEFT', + ['campaign.id', '=', 'contribution.campaign_id'] + ) + ->addWhere('contact_id', '=', $contactId) + ->addWhere('type', '=', 'OOFF') + ->execute(); + foreach ($ooffMandates as $ooffMandate) { + $ooffList[] = [ + 'receive_date' => $ooffMandate['contribution.receive_date'], + 'status_raw' => $ooffMandate['status'], + 'status' => CRM_Sepa_Logic_Status::translateMandateStatus($ooffMandate['status'], TRUE), + 'reference' => $ooffMandate['reference'], + 'financial_type' => $ooffMandate['contribution.financial_type_id:name'], + 'campaign' => $ooffMandate['campaign.title'], + 'total_amount' => $ooffMandate['contribution.total_amount'], + 'currency' => $ooffMandate['contribution.currency'], + 'cancel_reason' => $ooffMandate['contribution.cancel_reason'], + 'class' => 'OOFF' === $ooffMandate['status'] ? 'sepa-active' : 'sepa-inactive', + 'view_link' => CRM_Utils_System::url( + 'civicrm/contact/view/contribution', + "reset=1&id={$ooffMandate['contribution.id']}&cid={$contactId}&action=view&context=contribution" + ), + 'edit_link' => CRM_Utils_System::url( + 'civicrm/sepa/xmandate', + "mid={$ooffMandate['id']}" + ), + ]; } - $this->assign('ooffs', $ooff_list); - - - // ============================== - // == RCUR == - // ============================== - $rcur_list = array(); - $rcur_query = " - SELECT - civicrm_sdd_mandate.id AS mandate_id, - civicrm_contribution_recur.id AS rcur_id, - civicrm_contribution_recur.start_date AS start_date, - civicrm_contribution_recur.end_date AS end_date, - civicrm_contribution_recur.next_sched_contribution_date AS next_collection_date, - last.receive_date AS last_collection_date, - last.cancel_reason AS last_cancel_reason, - civicrm_sdd_mandate.status AS status, - civicrm_sdd_mandate.reference AS reference, - cancel_reason.note AS cancel_reason, - civicrm_financial_type.name AS financial_type, - civicrm_campaign.title AS campaign, - civicrm_sdd_mandate.reference AS reference, - civicrm_contribution_recur.frequency_interval AS frequency_interval, - civicrm_contribution_recur.frequency_unit AS frequency_unit, - civicrm_contribution_recur.cycle_day AS cycle_day, - civicrm_contribution_recur.currency AS currency, - civicrm_contribution_recur.amount AS amount, - IF(civicrm_sdd_mandate.status IN ('FRST', 'RCUR'), 'sepa-active', 'sepa-inactive') - AS class - FROM civicrm_sdd_mandate - LEFT JOIN civicrm_contribution_recur ON civicrm_contribution_recur.id = civicrm_sdd_mandate.entity_id - LEFT JOIN civicrm_financial_type ON civicrm_financial_type.id = civicrm_contribution_recur.financial_type_id - LEFT JOIN civicrm_campaign ON civicrm_campaign.id = civicrm_contribution_recur.campaign_id - LEFT JOIN civicrm_contribution last ON last.receive_date = (SELECT MAX(receive_date) FROM civicrm_contribution - WHERE contribution_recur_id = civicrm_contribution_recur.id - AND contribution_status_id != 2) - LEFT JOIN civicrm_note cancel_reason ON cancel_reason.entity_id = civicrm_contribution_recur.id - AND cancel_reason.entity_table = 'civicrm_contribution_recur' - AND cancel_reason.subject = 'cancel_reason' - WHERE civicrm_sdd_mandate.contact_id = %1 - AND civicrm_sdd_mandate.type = 'RCUR' - AND civicrm_sdd_mandate.entity_table = 'civicrm_contribution_recur' - GROUP BY civicrm_sdd_mandate.id - ORDER BY civicrm_contribution_recur.start_date DESC, civicrm_sdd_mandate.id DESC;"; - - $mandate_ids = array(); - - CRM_Core_DAO::disableFullGroupByMode(); - $rcur_mandates = CRM_Core_DAO::executeQuery($rcur_query, - array(1 => array($contact_id, 'Integer')) - ); - CRM_Core_DAO::reenableFullGroupByMode(); - - while ($rcur_mandates->fetch()) { - $rcur = array( - 'mandate_id' => $rcur_mandates->mandate_id, - 'start_date' => $rcur_mandates->start_date, - 'cycle_day' => $rcur_mandates->cycle_day, - 'status_raw' => $rcur_mandates->status, - 'reference' => $rcur_mandates->reference, - 'financial_type' => $rcur_mandates->financial_type, - 'campaign' => $rcur_mandates->campaign, - 'status' => CRM_Sepa_Logic_Status::translateMandateStatus($rcur_mandates->status, TRUE), - 'frequency' => CRM_Utils_SepaOptionGroupTools::getFrequencyText($rcur_mandates->frequency_interval, $rcur_mandates->frequency_unit, TRUE), - 'next_collection_date' => $rcur_mandates->next_collection_date, - 'last_collection_date' => $rcur_mandates->last_collection_date, - 'cancel_reason' => $rcur_mandates->cancel_reason, - 'last_cancel_reason' => $rcur_mandates->last_cancel_reason, - 'reference' => $rcur_mandates->reference, - 'end_date' => $rcur_mandates->end_date, - 'currency' => $rcur_mandates->currency, - 'amount' => $rcur_mandates->amount, - 'class' => $rcur_mandates->class - ); - - // calculate annual amount - if ($rcur_mandates->frequency_unit == 'year') { - $rcur['total_amount'] = $rcur_mandates->amount / $rcur_mandates->frequency_interval; - } elseif ($rcur_mandates->frequency_unit == 'month') { - $rcur['total_amount'] = $rcur_mandates->amount * 12.0 / $rcur_mandates->frequency_interval; + $this->assign('ooffs', $ooffList); + + + // Retrieve RCUR mandates. + $rcurList = []; + $rcurMandates = SepaMandate::get(TRUE) + ->addSelect( + 'id', + 'contribution_recur.id', + 'contribution_recur.start_date', + 'contribution_recur.end_date', + 'contribution_recur.next_sched_contribution_date', + 'last_contribution.cancel_reason', + 'status', + 'reference', + 'GROUP_FIRST(cancel_reason.note) AS cancel_reason', + 'contribution_recur.financial_type_id:name', + 'campaign.title', + 'contribution_recur.frequency_interval', + 'contribution_recur.frequency_unit', + 'contribution_recur.cycle_day', + 'contribution_recur.currency', + 'contribution_recur.amount' + ) + ->addJoin( + 'ContributionRecur AS contribution_recur', + 'INNER', + ['entity_id', '=', 'contribution_recur.id'], + ['entity_table', '=', '"civicrm_contribution_recur"'] + ) + ->addJoin( + 'Note AS cancel_reason', + 'LEFT', + ['cancel_reason.entity_id', '=', 'contribution_recur.id'], + ['cancel_reason.entity_table', '=', '"civicrm_contribution_recur"'], + ['cancel_reason.subject', '=', '"cancel_reason"'] + ) + ->addJoin( + 'Campaign AS campaign', + 'LEFT', + ['campaign.id', '=', 'contribution_recur.campaign_id'] + ) + ->addWhere('contact_id', '=', $contactId) + ->addWhere('type', '=', 'RCUR') + ->addWhere('entity_table', '=', 'civicrm_contribution_recur') + ->addOrderBy('contribution_recur.start_date', 'DESC') + ->addOrderBy('id', 'DESC') + ->addGroupBy('id') + ->execute(); + + foreach ($rcurMandates as $rcurMandate) { + $lastInstallment = Contribution::get() + ->addSelect('receive_date', 'cancel_reason') + ->addWhere('contribution_recur_id', '=', $rcurMandate['contribution_recur.id']) + ->addWhere('contribution_status_id:name', '!=', 'Pending') + ->addOrderBy('receive_date', 'DESC') + ->setLimit(1) + ->execute() + ->first(); + $rcurRow = [ + 'mandate_id' => $rcurMandate['id'], + 'start_date' => $rcurMandate['contribution_recur.start_date'], + 'cycle_day' => $rcurMandate['contribution_recur.cycle_day'], + 'status_raw' => $rcurMandate['status'], + 'reference' => $rcurMandate['reference'], + 'financial_type' => $rcurMandate['contribution_recur.financial_type_id:name'], + 'campaign' => $rcurMandate['campaign.title'], + 'status' => CRM_Sepa_Logic_Status::translateMandateStatus($rcurMandate['status'], TRUE), + 'frequency' => CRM_Utils_SepaOptionGroupTools::getFrequencyText( + $rcurMandate['contribution_recur.frequency_interval'], + $rcurMandate['contribution_recur.frequency_unit'], + TRUE + ), + 'next_collection_date' => $rcurMandate['contribution_recur.next_sched_contribution_date'], + 'last_collection_date' => $lastInstallment['receive_date'] ?? NULL, + 'cancel_reason' => $rcur_mandates['cancel_reason'], + 'last_cancel_reason' => $lastInstallment['cancel_reason'] ?? NULL, + 'end_date' => $rcurMandate['contribution_recur.end_date'], + 'currency' => $rcurMandate['contribution_recur.currency'], + 'amount' => $rcurMandate['contribution_recur.amount'], + 'class' => in_array($rcurMandate['status'], ['FRST', 'RCUR']) + ? 'sepa-active' + : 'sepa-inactive', + ]; + + // Calculate annual amount. + if ('year' === $rcurMandate['contribution_recur.frequency_unit']) { + $rcurRow['total_amount'] = + $rcurMandate['contribution_recur.amount'] / $rcurMandate['contribution_recur.frequency_interval']; + } elseif ('month' === $rcurMandate['contribution_recur.frequency_unit']) { + $rcurRow['total_amount'] = + $rcurMandate['contribution_recur.amount'] * 12.0 / $rcurMandate['contribution_recur.frequency_interval']; } - // add links - $rcur['view_link'] = CRM_Utils_System::url('civicrm/contact/view/contributionrecur', "reset=1&id={$rcur_mandates->rcur_id}&cid={$contact_id}&context=contribution"); + // Add links. + $rcurRow['view_link'] = CRM_Utils_System::url( + 'civicrm/contact/view/contributionrecur', + "reset=1&id={$rcurMandate['contribution_recur.id']}&cid={$contactId}&context=contribution"); if (CRM_Core_Permission::check('edit sepa mandates')) { - $rcur['edit_link'] = CRM_Utils_System::url('civicrm/sepa/xmandate', "mid={$rcur_mandates->mandate_id}"); + $rcurRow['edit_link'] = CRM_Utils_System::url('civicrm/sepa/xmandate', "mid={$rcurMandate['id']}"); } - $rcur_list[$rcur_mandates->mandate_id] = $rcur; + $rcurList[$rcurMandate['id']] = $rcurRow; } - // add cancellation info - if (!empty($rcur_list)) { - $mandate_id_list = implode(',', array_keys($rcur_list)); + // Add cancellation info. + // TODO: Transform into APIv4 query. + // This currently generates a string of "0" (for pending/in progress/completed contributions) and "1" for + // other status and counts trailing "1"s, passing the number of failed last installments to the template. + // As this does not disclose contribution information that has not yet been fetched via the API, no additional + // ACL bypassing is being done here. + if (!empty($rcurList)) { + $mandate_id_list = implode(',', array_keys($rcurList)); $fail_sequence = " SELECT civicrm_sdd_mandate.id AS mandate_id, @@ -196,12 +231,12 @@ public function run() { while ($fail_query->fetch()) { if (preg_match("#(?1+)$#", $fail_query->fail_sequence, $match)) { $last_sequence = $match['last_fails']; - $rcur_list[$fail_query->mandate_id]['fail_sequence'] = strlen($last_sequence); + $rcurList[$fail_query->mandate_id]['fail_sequence'] = strlen($last_sequence); } } } - $this->assign('rcurs', $rcur_list); + $this->assign('rcurs', $rcurList); parent::run(); } diff --git a/CRM/Sepa/Page/MarkGroupReceived.php b/CRM/Sepa/Page/MarkGroupReceived.php index b41e231b..bb6ab36f 100644 --- a/CRM/Sepa/Page/MarkGroupReceived.php +++ b/CRM/Sepa/Page/MarkGroupReceived.php @@ -38,7 +38,10 @@ function run() { } // get the group - $group = civicrm_api3('SepaTransactionGroup', 'getsingle', ['id' => $group_id]); + $group = \Civi\Api4\SepaTransactionGroup::get(TRUE) + ->addWhere('id', '=', $group_id) + ->execute() + ->single(); $this->assign('txgroup', $group); // check whether this is a group created by a test creditor diff --git a/CRM/Sepa/Upgrader.php b/CRM/Sepa/Upgrader.php index bc73b238..179e3fa6 100644 --- a/CRM/Sepa/Upgrader.php +++ b/CRM/Sepa/Upgrader.php @@ -15,7 +15,7 @@ +--------------------------------------------------------*/ use CRM_Sepa_ExtensionUtil as E; - + /** * Collection of upgrade steps. */ @@ -491,4 +491,24 @@ public function upgrade_1804() { $customData->syncOptionGroup(E::path('resources/formats_option_group.json')); return TRUE; } + + public function upgrade_11301() { + $this->ctx->log->info('Adding financial_type_id column to civicrm_sdd_txgroup table.'); + $column = CRM_Core_DAO::singleValueQuery( + <<executeSql( + <<invoke($names, $reference, $creditor_id, $mode, $collection_date, self::$null, self::$null, 'civicrm_modify_txgroup_reference'); + static function modify_txgroup_reference(&$reference, $creditor_id, $mode, $collection_date, $financial_type_id) { + $names = ['reference', 'creditor_id', 'mode', 'collection_date', 'financial_type_id']; + return CRM_Utils_Hook::singleton()->invoke($names, $reference, $creditor_id, $mode, $collection_date, $financial_type_id, self::$null, 'civicrm_modify_txgroup_reference'); } diff --git a/Civi/Api4/SepaContributionGroup.php b/Civi/Api4/SepaContributionGroup.php new file mode 100644 index 00000000..0b260d24 --- /dev/null +++ b/Civi/Api4/SepaContributionGroup.php @@ -0,0 +1,45 @@ +. + */ + +declare(strict_types = 1); + +namespace Civi\Api4; + +use Civi\Api4\Generic\DAOEntity; + +/** + * SepaContributionGroup entity. + * + * Provided by the CiviSEPA extension. + * + * @package Civi\Api4 + */ +class SepaContributionGroup extends Generic\DAOEntity { + use Generic\Traits\EntityBridge; + + public static function permissions(): array { + return [ + 'get' => ['view sepa groups'], + 'create' => ['batch sepa groups'], + 'update' => ['batch sepa groups'], + 'delete' => [ + ['batch sepa groups', 'delete sepa groups'], + ], + ]; + } + +} diff --git a/Civi/Api4/SepaCreditor.php b/Civi/Api4/SepaCreditor.php index c0424e65..f9f7f486 100644 --- a/Civi/Api4/SepaCreditor.php +++ b/Civi/Api4/SepaCreditor.php @@ -2,9 +2,9 @@ namespace Civi\Api4; /** - * Resource entity. + * SepaCreditor entity. * - * Provided by the CiviCRM Resource Management extension. + * Provided by the CiviSEPA extension. * * @package Civi\Api4 */ diff --git a/Civi/Api4/SepaMandate.php b/Civi/Api4/SepaMandate.php index 8f483fe0..386f0500 100644 --- a/Civi/Api4/SepaMandate.php +++ b/Civi/Api4/SepaMandate.php @@ -1,15 +1,22 @@ setCheckPermissions($checkPermissions); + } + /** * @see \Civi\Api4\Generic\AbstractEntity::permissions() * @return array[] @@ -20,5 +27,5 @@ public static function permissions(): array { 'update' => ['edit sepa mandates'], ]; } - + } diff --git a/Civi/Api4/SepaSddFile.php b/Civi/Api4/SepaSddFile.php index 0940b1b0..2b52d7ac 100644 --- a/Civi/Api4/SepaSddFile.php +++ b/Civi/Api4/SepaSddFile.php @@ -2,12 +2,23 @@ namespace Civi\Api4; /** - * Resource entity. + * SepaSddFile entity. * - * Provided by the CiviCRM Resource Management extension. + * Provided by the CiviSEPA extension. * * @package Civi\Api4 */ class SepaSddFile extends Generic\DAOEntity { + public static function permissions(): array { + return [ + 'get' => ['view sepa groups'], + 'create' => ['batch sepa groups'], + 'update' => ['batch sepa groups'], + 'delete' => [ + ['batch sepa groups', 'delete sepa groups'], + ], + ]; + } + } diff --git a/Civi/Api4/SepaTransactionGroup.php b/Civi/Api4/SepaTransactionGroup.php index f97c08db..916575d4 100644 --- a/Civi/Api4/SepaTransactionGroup.php +++ b/Civi/Api4/SepaTransactionGroup.php @@ -1,13 +1,29 @@ setCheckPermissions($checkPermissions); + } + + public static function permissions(): array { + return [ + 'get' => ['view sepa groups'], + 'create' => ['batch sepa groups'], + 'update' => ['batch sepa groups'], + 'delete' => ['delete sepa groups'], + ]; + } + } diff --git a/Civi/Sepa/ActionProvider/Action/CreateOneOffMandate.php b/Civi/Sepa/ActionProvider/Action/CreateOneOffMandate.php index fbd214f4..325a9bd6 100644 --- a/Civi/Sepa/ActionProvider/Action/CreateOneOffMandate.php +++ b/Civi/Sepa/ActionProvider/Action/CreateOneOffMandate.php @@ -22,6 +22,7 @@ use \Civi\ActionProvider\Parameter\Specification; use \Civi\ActionProvider\Parameter\SpecificationBag; +use Civi\Api4\SepaMandate; use CRM_Sepa_ExtensionUtil as E; class CreateOneOffMandate extends AbstractAction { @@ -126,7 +127,11 @@ protected function doAction(ParameterBagInterface $parameters, ParameterBagInter // create mandate try { $mandate = \civicrm_api3('SepaMandate', 'createfull', $mandate_data); - $mandate = \civicrm_api3('SepaMandate', 'getsingle', ['id' => $mandate['id'], 'return' => 'id,reference']); + $mandate = SepaMandate::get(TRUE) + ->addSelect('id', 'reference') + ->addWhere('id', '=', $mandate['id']) + ->execute() + ->single(); $output->setParameter('mandate_id', $mandate['id']); $output->setParameter('mandate_reference', $mandate['reference']); $output->setParameter('error', ''); diff --git a/Civi/Sepa/ActionProvider/Action/CreateRecurringMandate.php b/Civi/Sepa/ActionProvider/Action/CreateRecurringMandate.php index 0b1eedbe..dd8415d2 100644 --- a/Civi/Sepa/ActionProvider/Action/CreateRecurringMandate.php +++ b/Civi/Sepa/ActionProvider/Action/CreateRecurringMandate.php @@ -21,6 +21,7 @@ use \Civi\ActionProvider\Parameter\Specification; use \Civi\ActionProvider\Parameter\SpecificationBag; +use Civi\Api4\SepaMandate; use CRM_Sepa_ExtensionUtil as E; class CreateRecurringMandate extends CreateOneOffMandate { @@ -150,7 +151,11 @@ protected function doAction(ParameterBagInterface $parameters, ParameterBagInter // create mandate try { $mandate = \civicrm_api3('SepaMandate', 'createfull', $mandate_data); - $mandate = \civicrm_api3('SepaMandate', 'getsingle', ['id' => $mandate['id'], 'return' => 'id,reference,entity_id']); + $mandate = SepaMandate::get(TRUE) + ->addSelect('id', 'reference', 'entity_id') + ->addWhere('id', '=', $mandate['id']) + ->execute() + ->single(); $output->setParameter('mandate_id', $mandate['id']); $output->setParameter('recurring_contribution_id', $mandate['entity_id']); $output->setParameter('mandate_reference', $mandate['reference']); diff --git a/Civi/Sepa/ActionProvider/Action/FindMandate.php b/Civi/Sepa/ActionProvider/Action/FindMandate.php index 72f7b0e8..aad7ef2a 100644 --- a/Civi/Sepa/ActionProvider/Action/FindMandate.php +++ b/Civi/Sepa/ActionProvider/Action/FindMandate.php @@ -20,6 +20,7 @@ use \Civi\ActionProvider\Parameter\Specification; use \Civi\ActionProvider\Parameter\SpecificationBag; +use Civi\Api4\SepaMandate; use CRM_Sepa_ExtensionUtil as E; class FindMandate extends CreateRecurringMandate { @@ -106,37 +107,37 @@ public function getOutputSpecification() { protected function doAction(ParameterBagInterface $parameters, ParameterBagInterface $output) { // compile search query - $mandate_search = []; + $mandatesQuery = SepaMandate::get(TRUE); if (!empty($this->configuration->getParameter('creditor_id'))) { - $mandate_search['creditor_id'] = ['IN' => $this->configuration->getParameter('creditor_id')]; + $mandatesQuery->addWhere('creditor_id', 'IN', $this->configuration->getParameter('creditor_id')); } if (!empty($this->configuration->getParameter('type'))) { - $mandate_search['type'] = $this->configuration->getParameter('type'); + $mandatesQuery->addWhere('type', '=', $this->configuration->getParameter('type')); } if (!empty($this->configuration->getParameter('active'))) { - $mandate_search['status'] = ['IN' => ['FRST', 'RCUR', 'OOFF', 'INIT']]; + $mandatesQuery->addWhere('status', 'IN', ['FRST', 'RCUR', 'OOFF', 'INIT']); } if (!empty($parameters->getParameter('contact_id'))) { - $mandate_search['contact_id'] = $parameters->getParameter('contact_id'); + $mandatesQuery->addWhere('contact_id', '=', $parameters->getParameter('contact_id')); } if (!empty($parameters->getParameter('account_holder'))) { - $mandate_search['account_holder'] = $parameters->getParameter('account_holder'); + $mandatesQuery->addWhere('account_holder', '=', $parameters->getParameter('account_holder')); } if (!empty($parameters->getParameter('iban'))) { - $mandate_search['iban'] = $parameters->getParameter('iban'); + $mandatesQuery->addWhere('iban', '=', $parameters->getParameter('iban')); } if (!empty($parameters->getParameter('reference'))) { - $mandate_search['reference'] = $parameters->getParameter('reference'); + $mandatesQuery->addWhere('reference', '=', $parameters->getParameter('reference')); } // add order - $mandate_search['option.sort'] = $this->configuration->getParameter('pick'); - $mandate_search['option.limit'] = 1; + $mandatesQuery->addOrderBy($this->configuration->getParameter('pick')); + // TODO: Shouldn't this use single()? + $mandatesQuery->setLimit(1); // search mandate - $result = \civicrm_api3('SepaMandate', 'get', $mandate_search); - if ($result['count']) { - $mandate = reset($result['values']); + $mandate = $mandatesQuery->execute()->first(); + if (isset($mandate)) { $output->setParameter('id', $mandate['id']); $output->setParameter('reference', $mandate['reference']); $output->setParameter('type', $mandate['type']); @@ -191,4 +192,4 @@ protected function doAction(ParameterBagInterface $parameters, ParameterBagInter } } } -} \ No newline at end of file +} diff --git a/Civi/Sepa/ActionProvider/Action/TerminateMandate.php b/Civi/Sepa/ActionProvider/Action/TerminateMandate.php index fab45494..547a3e60 100644 --- a/Civi/Sepa/ActionProvider/Action/TerminateMandate.php +++ b/Civi/Sepa/ActionProvider/Action/TerminateMandate.php @@ -22,6 +22,7 @@ use \Civi\ActionProvider\Parameter\SpecificationBag; use Civi\FormProcessor\API\Exception; +use Civi\Sepa\DataProcessor\Source\SepaMandate; use CRM_Sepa_ExtensionUtil as E; class TerminateMandate extends AbstractAction { @@ -79,10 +80,11 @@ protected function doAction(ParameterBagInterface $parameters, ParameterBagInter if ($mandateReference) { // find mandate ID with reference try { - $mandateId = civicrm_api3('SepaMandate', 'getvalue', [ - 'return' => "id", - 'reference' => $mandateReference, - ]); + $mandateId = \Civi\Api4\SepaMandate::get(TRUE) + ->addSelect('id') + ->addWhere('reference', '=', $mandateReference) + ->execute() + ->single()['id']; if ($mandateId) { $output->setParameter('mandate_id', $mandateId); // terminate mandate with cancel reason from parameter if provided else condiguration cancel reason diff --git a/Civi/Sepa/Api4/Action/SepaMandate/GetAction.php b/Civi/Sepa/Api4/Action/SepaMandate/GetAction.php new file mode 100644 index 00000000..bbb207a6 --- /dev/null +++ b/Civi/Sepa/Api4/Action/SepaMandate/GetAction.php @@ -0,0 +1,54 @@ +. + */ + +declare(strict_types = 1); + +namespace Civi\Sepa\Api4\Action\SepaMandate; + +use Civi\Api4\Generic\DAOGetAction; +use Civi\Api4\Generic\Result; + +class GetAction extends DAOGetAction { + + public function _run(Result $result) { + // Add unique joins for permission checks of Financial ACLs. + if ($this->getCheckPermissions()) { + $contributionAlias = uniqid('contribution_'); + $contributionRecurAlias = uniqid('contribution_recur_'); + $this + ->addJoin( + 'Contribution AS ' . $contributionAlias, + 'LEFT', + ['entity_table', '=', '"civicrm_contribution"'], + ['entity_id', '=', $contributionAlias . '.id'] + ) + ->addJoin( + 'ContributionRecur AS ' . $contributionRecurAlias, + 'LEFT', + ['entity_table', '=', '"civicrm_contribution_recur"'], + ['entity_id', '=', $contributionRecurAlias . '.id'] + ) + ->addClause( + 'OR', + ['AND', [['type', '=', 'OOFF'], [$contributionAlias . '.id', 'IS NOT NULL']]], + ['AND', [['type', '=', 'RCUR'], [$contributionRecurAlias . '.id', 'IS NOT NULL']]] + ); + } + return parent::_run($result); + } + +} diff --git a/Civi/Sepa/Api4/Action/SepaTransactionGroup/GetAction.php b/Civi/Sepa/Api4/Action/SepaTransactionGroup/GetAction.php new file mode 100644 index 00000000..76e0b7a2 --- /dev/null +++ b/Civi/Sepa/Api4/Action/SepaTransactionGroup/GetAction.php @@ -0,0 +1,49 @@ +. + */ + +declare(strict_types = 1); + +namespace Civi\Sepa\Api4\Action\SepaTransactionGroup; + +use Civi\Api4\Generic\DAOGetAction; +use Civi\Api4\Generic\Result; +use Civi\Api4\SepaContributionGroup; + +class GetAction extends DAOGetAction { + + public function _run(Result $result) { + if ($this->getCheckPermissions()) { + // Count permissioned contributions (in the join) and the total number of contributions in the transaction group, + // and extract those with matching counts, i.e. groups of which the user has permission to view all contributions. + $fullyPermissionedTxgroups = SepaContributionGroup::get() + ->addSelect( + 'txgroup_id', + 'COUNT(contribution.id) AS allowed_contributions', + 'COUNT(*) AS total_contributions' + ) + ->addJoin('Contribution AS contribution', 'LEFT', ['contribution.id', '=', 'contribution_id']) + ->addGroupBy('txgroup_id') + ->addHaving('allowed_contributions', '=', 'total_contributions', TRUE) + ->execute() + ->column('txgroup_id'); + $this + ->addWhere('id', 'IN', $fullyPermissionedTxgroups); + } + return parent::_run($result); + } + +} diff --git a/Civi/Sepa/Util/ContributionUtil.php b/Civi/Sepa/Util/ContributionUtil.php new file mode 100644 index 00000000..887dbaec --- /dev/null +++ b/Civi/Sepa/Util/ContributionUtil.php @@ -0,0 +1,58 @@ +. + */ + +declare(strict_types = 1); + +namespace Civi\Sepa\Util; + +use Civi\Api4\FinancialType; + +class ContributionUtil { + + /** + * Retrieves the list of (CiviSEPA) payment instruments. + * + * @return array + * An array of payment instrument labels, keyed by their ID. + */ + public static function getPaymentInstrumentList(): array { + $list = []; + $payment_instruments = \CRM_Sepa_Logic_PaymentInstruments::getAllSddPaymentInstruments(); + foreach ($payment_instruments as $payment_instrument) { + $list[$payment_instrument['id']] = $payment_instrument['label']; + } + + return $list; + } + + /** + * Retrieves the list of (active) financial types. + * + * @return array + * An array of financial type names, keyed by their ID. + */ + public static function getFinancialTypeList(): array { + // Check permissions for financial types for evaluating Financial ACLs. + return FinancialType::get() + ->addSelect('id', 'name') + ->addWhere('is_active', '=', TRUE) + ->execute() + ->indexBy('id') + ->column('name'); +} + +} diff --git a/api/v3/SepaAlternativeBatching.php b/api/v3/SepaAlternativeBatching.php index 70728350..92128236 100644 --- a/api/v3/SepaAlternativeBatching.php +++ b/api/v3/SepaAlternativeBatching.php @@ -130,7 +130,7 @@ function civicrm_api3_sepa_alternative_batching_update($params) { } else { $creditors = array(); foreach ($creditor_query['values'] as $creditor) { - $creditors[] = $creditor['id']; + $creditors[] = (int) $creditor['id']; } } diff --git a/api/v3/SepaMandate.php b/api/v3/SepaMandate.php index 5a29ec52..e1028d90 100644 --- a/api/v3/SepaMandate.php +++ b/api/v3/SepaMandate.php @@ -111,6 +111,14 @@ function civicrm_api3_sepa_mandate_createfull($params) { } } + // Validate financial type. + if ( + is_numeric($params['financial_type_id'] ?? NULL) + && !array_key_exists($params['financial_type_id'], \Civi\Sepa\Util\ContributionUtil::getFinancialTypeList()) + ) { + throw new CRM_Core_Exception("No permission for creating SEPA mandates with financial type [{$params['financial_type_id']}]."); + } + // if BIC is used for this creditor, it is required (see #245) if (empty($params['bic'])) { if ($creditor['uses_bic']) { @@ -433,10 +441,14 @@ function civicrm_api3_sepa_mandate_modify($params) { // look up mandate ID if only reference is given if (empty($params['mandate_id']) && !empty($params['reference'])) { - $mandate = civicrm_api3('SepaMandate', 'get', array('reference' => $params['reference'], 'return' => 'id')); - if ($mandate['id']) { - $params['mandate_id'] = $mandate['id']; - } else { + try { + $params['mandate_id'] = \Civi\Api4\SepaMandate::get(TRUE) + ->addSelect('id') + ->addWhere('reference', '=', $params['reference']) + ->execute() + ->single()['id']; + } + catch (\CRM_Core_Exception $exception) { return civicrm_api3_create_error("Couldn't identify mandate with reference '{$params['reference']}'."); } } @@ -511,10 +523,14 @@ function _civicrm_api3_sepa_mandate_modify_spec(&$params) { function civicrm_api3_sepa_mandate_terminate($params) { // look up mandate ID if only reference is given if (empty($params['mandate_id']) && !empty($params['reference'])) { - $mandate = civicrm_api3('SepaMandate', 'get', array('reference' => $params['reference'], 'return' => 'id')); - if ($mandate['id']) { - $params['mandate_id'] = $mandate['id']; - } else { + try { + $params['mandate_id'] = \Civi\Api4\SepaMandate::get(TRUE) + ->addSelect('id') + ->addWhere('reference', '=', $params['reference']) + ->execute() + ->single()['id']; + } + catch (\CRM_Core_Exception $exception) { return civicrm_api3_create_error("Couldn't identify mandate with reference '{$params['reference']}'."); } } diff --git a/api/v3/SepaTransactionGroup.php b/api/v3/SepaTransactionGroup.php index 59790a71..439d7f45 100644 --- a/api/v3/SepaTransactionGroup.php +++ b/api/v3/SepaTransactionGroup.php @@ -84,6 +84,9 @@ function civicrm_api3_sepa_transaction_group_get($params) { return _civicrm_api3_basic_get(_civicrm_api3_get_BAO(__FUNCTION__), $params); } +/** + * @deprecated This action will not be ported to APIv4. Use the "Get" action with custom joins and filters instead. + */ function civicrm_api3_sepa_transaction_group_getdetail($params) { // $where = "txgroup.id= txgroup_contrib.txgroup_id AND txgroup_contrib.contribution_id = contrib.id"; $where = "1"; @@ -239,9 +242,14 @@ function civicrm_api3_sepa_transaction_group_createnext ($params) { function civicrm_api3_sepa_transaction_group_toaccgroup($params) { // first, load the txgroup $txgroup_id = $params['txgroup_id']; - $txgroup = civicrm_api('SepaTransactionGroup', 'getsingle', array('id' => $txgroup_id, 'version' => 3)); - if (isset($txgroup['is_error']) && $txgroup['is_error']) { - return civicrm_api3_create_error("Cannot read transaction group ".$txgroup_id); + try { + $txgroup = \Civi\Api4\SepaTransactionGroup::get(TRUE) + ->addWhere('id', '=', $txgroup_id) + ->execute() + ->single(); + } + catch (\CRM_Core_Exception $exception) { + return civicrm_api3_create_error('Cannot read transaction group ' . $txgroup_id); } if (isset($txgroup['sdd_file_id'])) { diff --git a/info.xml b/info.xml index 4380a404..174da8a7 100644 --- a/info.xml +++ b/info.xml @@ -13,7 +13,7 @@ dev 5.75 - 5.65 + 5.69 https://github.com/Project60/org.project60.sepa diff --git a/l10n/de_DE/LC_MESSAGES/sepa.mo b/l10n/de_DE/LC_MESSAGES/sepa.mo index d28b7f2b..b9645a54 100644 Binary files a/l10n/de_DE/LC_MESSAGES/sepa.mo and b/l10n/de_DE/LC_MESSAGES/sepa.mo differ diff --git a/l10n/de_DE/LC_MESSAGES/sepa.po b/l10n/de_DE/LC_MESSAGES/sepa.po index 73303356..b2989acb 100644 --- a/l10n/de_DE/LC_MESSAGES/sepa.po +++ b/l10n/de_DE/LC_MESSAGES/sepa.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: Project60 SEPA DD\n" -"POT-Creation-Date: 2022-01-14 13:46+0100\n" +"POT-Creation-Date: \n" "PO-Revision-Date: \n" "Last-Translator: Bjoern Endres \n" "Language-Team: Project60 \n" @@ -10,7 +10,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Poedit-SourceCharset: utf-8\n" -"X-Generator: Poedit 2.0.6\n" +"X-Generator: Poedit 3.0.1\n" #: CRM/Admin/Form/Setting/SepaSettings.php #: templates/CRM/Admin/Form/Setting/SepaSettings.tpl @@ -60,6 +60,7 @@ msgid "Please enter the %s as number (integers only)." msgstr "Bitte geben Sie %s als ganze Zahl an." #: CRM/Admin/Form/Setting/SepaSettings.php +#: Civi/Sepa/DataProcessor/Join/AbstractMandateJoin.php msgid "- select -" msgstr "- wählen -" @@ -98,12 +99,28 @@ msgstr "Adresse" msgid "Country" msgstr "Land" +#: CRM/Admin/Form/Setting/SepaSettings.php CRM/Sepa/DAO/SEPAMandate.php +#: CRM/Sepa/Form/CreateMandate.php CRM/Sepa/Form/Report/SepaMandateGeneric.php +#: CRM/Sepa/Form/Search/SepaContactSearch.php CRM/Utils/SepaTokens.php +#: Civi/Sepa/ActionProvider/Action/CreateOneOffMandate.php +#: Civi/Sepa/ActionProvider/Action/CreateRecurringMandate.php +#: Civi/Sepa/ActionProvider/Action/FindMandate.php +#: templates/CRM/Contribute/Form/ContributionConfirm.sepa.tpl +#: templates/CRM/Contribute/Form/ContributionThankYou.sepa.tpl +#: templates/CRM/Event/Form/RegistrationConfirm.sepa.tpl +#: templates/CRM/Event/Form/RegistrationThankYou.sepa.tpl +#: templates/CRM/Sepa/Page/EditMandate.tpl +#: templates/Sepa/Contribute/Form/ContributionView.tpl +#: templates/Sepa/Contribute/Page/ContributionRecur.tpl +msgid "Account Holder" +msgstr "Kontoinhaber" + #: CRM/Admin/Form/Setting/SepaSettings.php CRM/Sepa/Form/CreateMandate.php #: CRM/Sepa/Form/Report/SepaMandateGeneric.php -#: CRM/Sepa/Form/Search/SepaContactSearch.php +#: CRM/Sepa/Form/Search/SepaContactSearch.php CRM/Utils/SepaTokens.php #: Civi/Sepa/ActionProvider/Action/CreateOneOffMandate.php #: Civi/Sepa/ActionProvider/Action/CreateRecurringMandate.php -#: Civi/Sepa/ActionProvider/Action/FindMandate.php js/CreateMandate.js sepa.php +#: Civi/Sepa/ActionProvider/Action/FindMandate.php js/CreateMandate.js #: templates/CRM/Admin/Form/Setting/SepaSettings.tpl #: templates/CRM/Contribute/Form/ContributionConfirm.sepa.tpl #: templates/CRM/Contribute/Form/ContributionThankYou.sepa.tpl @@ -117,10 +134,10 @@ msgstr "BIC" #: CRM/Admin/Form/Setting/SepaSettings.php CRM/Sepa/Form/CreateMandate.php #: CRM/Sepa/Form/Report/SepaMandateGeneric.php -#: CRM/Sepa/Form/Search/SepaContactSearch.php +#: CRM/Sepa/Form/Search/SepaContactSearch.php CRM/Utils/SepaTokens.php #: Civi/Sepa/ActionProvider/Action/CreateOneOffMandate.php #: Civi/Sepa/ActionProvider/Action/CreateRecurringMandate.php -#: Civi/Sepa/ActionProvider/Action/FindMandate.php js/CreateMandate.js sepa.php +#: Civi/Sepa/ActionProvider/Action/FindMandate.php js/CreateMandate.js #: templates/CRM/Admin/Form/Setting/SepaSettings.tpl #: templates/CRM/Contribute/Form/ContributionConfirm.sepa.tpl #: templates/CRM/Contribute/Form/ContributionThankYou.sepa.tpl @@ -132,16 +149,21 @@ msgstr "BIC" msgid "IBAN" msgstr "IBAN" +#: CRM/Admin/Form/Setting/SepaSettings.php +msgid "CUC (only from CBIBdySDDReq)" +msgstr "CUC (nur aus CBIBdySDDReq)" + #: CRM/Admin/Form/Setting/SepaSettings.php CRM/Sepa/DAO/SEPACreditor.php #: CRM/Sepa/Form/CreateMandate.php CRM/Sepa/Form/Report/SepaMandateOOFF.php -#: CRM/Sepa/Form/Report/SepaMandateRCUR.php sepa.php +#: CRM/Sepa/Form/Report/SepaMandateRCUR.php CRM/Utils/SepaTokens.php msgid "Currency" msgstr "Währung" #: CRM/Admin/Form/Setting/SepaSettings.php CRM/Sepa/DAO/SEPAMandate.php #: CRM/Sepa/DAO/SEPATransactionGroup.php #: CRM/Sepa/Form/Report/SepaMandateGeneric.php -#: CRM/Sepa/Form/Search/SepaContactSearch.php sepa.php +#: CRM/Sepa/Form/Search/SepaContactSearch.php CRM/Utils/SepaTokens.php +#: Civi/Sepa/DataProcessor/Join/AbstractMandateJoin.php #: templates/CRM/Sepa/Page/DashBoard.tpl templates/CRM/Sepa/Page/MandateTab.tpl msgid "Type" msgstr "Art" @@ -240,8 +262,7 @@ msgstr "" #: CRM/Sepa/BAO/SEPAMandate.php CRM/Sepa/Form/CreateMandate.php #: CRM/Sepa/Logic/Queue/Update.php CRM/Sepa/Logic/Status.php #: CRM/Sepa/Page/CloseGroup.php CRM/Sepa/Page/CreateMandate.php -#: CRM/Sepa/Page/DashBoard.php CRM/Sepa/Page/EditMandate.php -#: CRM/Sepa/Page/ListGroup.php js/SepaSettings.js +#: CRM/Sepa/Page/DashBoard.php CRM/Sepa/Page/EditMandate.php js/SepaSettings.js msgid "Error" msgstr "Fehler" @@ -318,6 +339,10 @@ msgstr "Mandat [%1] kann nicht angepasst werden, das Batching läuft gerade!" msgid "You can only modify RCUR mandates." msgstr "Sie können nur Mandate mit wiederkehrenden Zahlungen (RCUR) ersetzen." +#: CRM/Sepa/BAO/SEPAMandate.php +msgid "Account Holder changed from '%1' to '%2'" +msgstr "Kontoinhaber geändert von '%1' auf '%2'" + #: CRM/Sepa/BAO/SEPAMandate.php msgid "IBAN changed from '%1' to '%2'" msgstr "IBAN geändert von '%1' auf '%2'" @@ -331,10 +356,8 @@ msgid "Bank details changed" msgstr "Bankverbindung geändert" #: CRM/Sepa/BAO/SEPAMandate.php -#, fuzzy -#| msgid "Amount has to be positive." msgid "The amount has to be positive." -msgstr "Betrag muss positiv sein." +msgstr "Der Betrag muss positiv sein." #: CRM/Sepa/BAO/SEPAMandate.php msgid "Amount increased" @@ -372,6 +395,14 @@ msgstr "Kampagne geändert" msgid "Campaign changed from '%1' [%2] to '%3' [%4]." msgstr "Kampagne von '%1' [%2] auf '%3' [%4] geändert." +#: CRM/Sepa/BAO/SEPAMandate.php +msgid "Cycle day changed" +msgstr "Einzugstag geändert" + +#: CRM/Sepa/BAO/SEPAMandate.php +msgid "Cycle day changed from '%1' to '%2'." +msgstr "Einzugstag geändert von '%1' auf '%2'." + #: CRM/Sepa/BAO/SEPAMandate.php msgid "%1 pending contributions were adjusted as well." msgstr "%1 pending contributions were adjusted as well." @@ -430,38 +461,42 @@ msgstr "SEPA Zuwendungsgruppen" msgid "SEPAContribution Group" msgstr "SEPA Zuwendungsgruppe" +#: CRM/Sepa/DAO/SEPAContributionGroup.php CRM/Sepa/DAO/SEPACreditor.php +#: CRM/Sepa/DAO/SEPAMandate.php CRM/Sepa/DAO/SEPASddFile.php +#: CRM/Sepa/DAO/SEPATransactionGroup.php +#: templates/CRM/Admin/Form/Setting/SepaSettings.tpl +#: templates/CRM/Sepa/Page/ListGroup.tpl +msgid "ID" +msgstr "ID" + #: CRM/Sepa/DAO/SEPAContributionGroup.php msgid "primary key" -msgstr "" +msgstr "Primärschlüssel" #: CRM/Sepa/DAO/SEPAContributionGroup.php -#, fuzzy -#| msgid "Contribution ID" -msgid "FK to Contribution ID" +#: CRM/Sepa/Form/Report/SepaMandateOOFF.php +msgid "Contribution ID" msgstr "Zuwendungs-ID" +#: CRM/Sepa/DAO/SEPAContributionGroup.php +msgid "FK to Contribution ID" +msgstr "Fremdschlüssel zu Zuwendungs-ID" + +#: CRM/Sepa/DAO/SEPAContributionGroup.php +msgid "Txgroup ID" +msgstr "Transaktionsgruppen-ID" + #: CRM/Sepa/DAO/SEPAContributionGroup.php msgid "FK to civicrm_sdd_txgroup" -msgstr "" +msgstr "Fremdschlüssel zu civicrm_sdd_txgroup" #: CRM/Sepa/DAO/SEPACreditor.php -#, fuzzy -#| msgid "SEPA Creditor" msgid "SEPACreditors" -msgstr "SEPA Gläubiger" +msgstr "SEPA-Gläubiger" #: CRM/Sepa/DAO/SEPACreditor.php -#, fuzzy -#| msgid "SEPA Creditor" msgid "SEPACreditor" -msgstr "SEPA Gläubiger" - -#: CRM/Sepa/DAO/SEPACreditor.php CRM/Sepa/DAO/SEPAMandate.php -#: CRM/Sepa/DAO/SEPASddFile.php CRM/Sepa/DAO/SEPATransactionGroup.php -#: templates/CRM/Admin/Form/Setting/SepaSettings.tpl -#: templates/CRM/Sepa/Page/ListGroup.tpl -msgid "ID" -msgstr "ID" +msgstr "SEPA-Gläubiger" #: CRM/Sepa/DAO/SEPACreditor.php msgid "Creditor Contact ID" @@ -469,7 +504,7 @@ msgstr "Gläubiger-Kontakt" #: CRM/Sepa/DAO/SEPACreditor.php msgid "FK to Contact ID that owns that account" -msgstr "" +msgstr "Fremdschlüssel zu Kontakt-ID des Kontoinhabers" #: CRM/Sepa/DAO/SEPACreditor.php msgid "SEPA Creditor identifier" @@ -480,6 +515,8 @@ msgid "" "Provided by the bank. ISO country code+check digit+ZZZ+country specific " "identifier" msgstr "" +"Von der Bank bereitgestellt. ISO-Ländercode + Prüfziffer + ZZZ + " +"länderspezifischer Identifikator" #: CRM/Sepa/DAO/SEPACreditor.php #: templates/CRM/Admin/Form/Setting/SepaSettings.tpl @@ -488,7 +525,7 @@ msgstr "Name des Gläubigers" #: CRM/Sepa/DAO/SEPACreditor.php msgid "official creditor name, passed to exported files" -msgstr "" +msgstr "offizieller Gläubigername, wird exportierten Dateien übergeben" #: CRM/Sepa/DAO/SEPACreditor.php #: templates/CRM/Admin/Form/Setting/SepaSettings.tpl @@ -496,8 +533,6 @@ msgid "Creditor Label" msgstr "Bezeichnung des Gläubigers" #: CRM/Sepa/DAO/SEPACreditor.php -#, fuzzy -#| msgid "internal label of the creditor" msgid "internally used label for the creditor" msgstr "Interner Name des Gläubigers" @@ -511,59 +546,49 @@ msgstr "" #: CRM/Sepa/DAO/SEPACreditor.php msgid "Which Country does this address belong to." -msgstr "" +msgstr "Zu welchem Land gehört diese Adresse" #: CRM/Sepa/DAO/SEPACreditor.php CRM/Sepa/DAO/SEPAMandate.php msgid "Iban" msgstr "IBAN" #: CRM/Sepa/DAO/SEPACreditor.php -#, fuzzy -#| msgid "name of the creditor" msgid "Iban of the creditor" -msgstr "Öffentlicher Name des Gläubigers" +msgstr "IBAN des Gläubigers" #: CRM/Sepa/DAO/SEPACreditor.php CRM/Sepa/DAO/SEPAMandate.php msgid "Bic" msgstr "BIC" #: CRM/Sepa/DAO/SEPACreditor.php -#, fuzzy -#| msgid "name of the creditor" msgid "BIC of the creditor" -msgstr "Öffentlicher Name des Gläubigers" +msgstr "BIC des Gläubigers" #: CRM/Sepa/DAO/SEPACreditor.php -#, fuzzy -#| msgid "Mandate numbering prefix" msgid "Mandate numering prefix" -msgstr "Mandats-Prefix" +msgstr "Mandats-Präfix" #: CRM/Sepa/DAO/SEPACreditor.php -#, fuzzy -#| msgid "Creditor Identifier" msgid "prefix for mandate identifiers" -msgstr "SEPA Gläubiger-Identifikationsnummer" +msgstr "Präfix für Mandatsreferenzen" #: CRM/Sepa/DAO/SEPACreditor.php -#, fuzzy -#| msgid "Currency used by this creditor" msgid "currency used by this creditor" -msgstr "Währung dieses Kreditors" +msgstr "Währung dieses Gläubigers" + +#: CRM/Sepa/DAO/SEPACreditor.php +msgid "Payment Processor ID" +msgstr "Zahlungsprozessor-ID" #: CRM/Sepa/DAO/SEPACreditor.php -#, fuzzy -#| msgid "First Contribution (to be deprecated)" msgid "Payment processor link (to be deprecated)" -msgstr "Ersteinzug" +msgstr "Zahlungsprozessor-Link (wird veralten)" #: CRM/Sepa/DAO/SEPACreditor.php msgid "Category purpose of the collection" msgstr "" #: CRM/Sepa/DAO/SEPACreditor.php -#, fuzzy -#| msgid "Default Creditor" msgid "Default value" msgstr "Standard-Gläubiger" @@ -607,10 +632,8 @@ msgid "Creditor Type" msgstr "Art des Kreditors" #: CRM/Sepa/DAO/SEPACreditor.php -#, fuzzy -#| msgid "Creditor Type: SEPA (default) or PSP" msgid "Type of the creditor, values are SEPA (default) and PSP" -msgstr "Art des Kreditors: SEPA (Standard) oder PSP " +msgstr "Art des Gläubigers: SEPA (Standard) oder PSP" #: CRM/Sepa/DAO/SEPACreditor.php msgid "OOFF Payment Instruments" @@ -631,26 +654,29 @@ msgid "" msgstr "" #: CRM/Sepa/DAO/SEPACreditor.php -#, fuzzy -#| msgid "Currency used by this creditor" msgid "If true, BICs are not used for this creditor" -msgstr "Währung dieses Kreditors" +msgstr "Falls aktiviert, werden BICs nicht für diesen Gläubiger verwendet" + +#: CRM/Sepa/DAO/SEPACreditor.php +#: templates/CRM/Admin/Form/Setting/SepaSettings.tpl +msgid "CUC" +msgstr "" + +#: CRM/Sepa/DAO/SEPACreditor.php +msgid "CUC of the creditor" +msgstr "CUC des Gläubigers" #: CRM/Sepa/DAO/SEPAMandate.php -#, fuzzy -#| msgid "SEPA Mandates" msgid "SEPAMandates" -msgstr "SEPA Mandate" +msgstr "SEPA-Mandate" #: CRM/Sepa/DAO/SEPAMandate.php -#, fuzzy -#| msgid "SEPA Mandate" msgid "SEPAMandate" msgstr "SEPA-Mandat" #: CRM/Sepa/DAO/SEPAMandate.php CRM/Sepa/DAO/SEPASddFile.php #: CRM/Sepa/DAO/SEPATransactionGroup.php CRM/Sepa/Form/CreateMandate.php -#: CRM/Sepa/Form/Search/SepaContactSearch.php sepa.php +#: CRM/Sepa/Form/Search/SepaContactSearch.php CRM/Utils/SepaTokens.php #: templates/CRM/Sepa/Page/EditMandate.tpl #: templates/CRM/Sepa/Page/MandateTab.tpl #: templates/Sepa/Contribute/Form/ContributionView.tpl @@ -659,14 +685,14 @@ msgid "Reference" msgstr "Mandatsreferenz" #: CRM/Sepa/DAO/SEPAMandate.php -#, fuzzy -#| msgid "Mandate reference" msgid "A unique mandate reference" -msgstr "Mandatsreferenz" +msgstr "eine eindeutige Mandatsreferenz" #: CRM/Sepa/DAO/SEPAMandate.php CRM/Sepa/Form/CreateMandate.php #: CRM/Sepa/Form/Report/SepaMandateGeneric.php -#: CRM/Sepa/Form/Report/SepaMandateOOFF.php sepa.php +#: CRM/Sepa/Form/Report/SepaMandateOOFF.php CRM/Utils/SepaTokens.php +#: Civi/Sepa/ActionProvider/Action/CreateOneOffMandate.php +#: Civi/Sepa/ActionProvider/Action/CreateRecurringMandate.php #: templates/CRM/Sepa/Page/CreateMandate.tpl #: templates/CRM/Sepa/Page/EditMandate.tpl #: templates/Sepa/Contribute/Form/ContributionView.tpl @@ -714,6 +740,10 @@ msgstr "Gläubiger-ID" msgid "FK to ssd_creditor" msgstr "" +#: CRM/Sepa/DAO/SEPAMandate.php +msgid "Creator" +msgstr "Ersteller" + #: CRM/Sepa/DAO/SEPAMandate.php CRM/Sepa/Form/Report/SepaMandateGeneric.php #: CRM/Sepa/Form/Search/SepaContactSearch.php #: Civi/Sepa/ActionProvider/Action/CreateOneOffMandate.php @@ -726,24 +756,30 @@ msgstr "Kontakt ID" msgid "FK to Contact ID of the debtor" msgstr "" +#: CRM/Sepa/DAO/SEPAMandate.php templates/CRM/Sepa/Page/CreateMandate.tpl +#: templates/CRM/Sepa/Page/EditMandate.tpl +#: templates/CRM/Sepa/Page/ListGroup.tpl +msgid "Contact" +msgstr "Kontakt" + +#: CRM/Sepa/DAO/SEPAMandate.php +msgid "Name of the account holder" +msgstr "Name des Kontoinhabers" + #: CRM/Sepa/DAO/SEPAMandate.php -#, fuzzy -#| msgid "name of the creditor" msgid "Iban of the debtor" -msgstr "Öffentlicher Name des Gläubigers" +msgstr "IBAN des Schuldners" #: CRM/Sepa/DAO/SEPAMandate.php -#, fuzzy -#| msgid "name of the creditor" msgid "BIC of the debtor" -msgstr "Öffentlicher Name des Gläubigers" +msgstr "BIC des Schuldners" #: CRM/Sepa/DAO/SEPAMandate.php msgid "RCUR for recurrent (default), OOFF for one-shot" msgstr "" #: CRM/Sepa/DAO/SEPAMandate.php CRM/Sepa/Form/Search/SepaContactSearch.php -#: Civi/Sepa/ActionProvider/Action/FindMandate.php sepa.php +#: CRM/Utils/SepaTokens.php Civi/Sepa/ActionProvider/Action/FindMandate.php #: templates/CRM/Sepa/Page/DashBoard.tpl #: templates/CRM/Sepa/Page/EditMandate.tpl #: templates/CRM/Sepa/Page/ListGroup.tpl templates/CRM/Sepa/Page/MandateTab.tpl @@ -762,31 +798,30 @@ msgstr "" msgid "creation date" msgstr "Erstellungsdatum" +#: CRM/Sepa/DAO/SEPAMandate.php CRM/Sepa/DAO/SEPASddFile.php +#: CRM/Sepa/DAO/SEPATransactionGroup.php +msgid "Created Date" +msgstr "Erstelldatum" + #: CRM/Sepa/DAO/SEPAMandate.php msgid "First Contribution (to be deprecated)" msgstr "Ersteinzug" #: CRM/Sepa/DAO/SEPAMandate.php -#, fuzzy -#| msgid "1st contribution" msgid "FK to civicrm_contribution" -msgstr "Erstzuwendung" +msgstr "Fremdschlüssel zu civicrm_contribution" #: CRM/Sepa/DAO/SEPAMandate.php msgid "validation date" msgstr "Validierungsdatum" #: CRM/Sepa/DAO/SEPASddFile.php -#, fuzzy -#| msgid "SEPA File Format" msgid "SEPASdd Files" -msgstr "SEPA Dateiformat (PAIN)" +msgstr "SEPASdd-Dateien" #: CRM/Sepa/DAO/SEPASddFile.php -#, fuzzy -#| msgid "SEPA File Format" msgid "SEPASdd File" -msgstr "SEPA Dateiformat (PAIN)" +msgstr "SEPASdd-Datei" #: CRM/Sepa/DAO/SEPASddFile.php msgid "End-to-end reference for this sdd file." @@ -797,33 +832,33 @@ msgid "Filename" msgstr "Dateiname" #: CRM/Sepa/DAO/SEPASddFile.php -#, fuzzy -#| msgid "name of the creditor" msgid "Name of the generated file" -msgstr "Öffentlicher Name des Gläubigers" +msgstr "Name der erzeugten Datei" #: CRM/Sepa/DAO/SEPASddFile.php CRM/Sepa/DAO/SEPATransactionGroup.php msgid "Latest Submission Date" msgstr "Spätestes Übertragungsdatum" #: CRM/Sepa/DAO/SEPASddFile.php CRM/Sepa/DAO/SEPATransactionGroup.php -#, fuzzy -#| msgid "Latest Submission Date" msgid "Latest submission date" msgstr "Spätestes Übertragungsdatum" -#: CRM/Sepa/DAO/SEPASddFile.php CRM/Sepa/DAO/SEPATransactionGroup.php -msgid "Created Date" -msgstr "Erstelldatum" - #: CRM/Sepa/DAO/SEPASddFile.php CRM/Sepa/DAO/SEPATransactionGroup.php msgid "When was this item created" msgstr "" +#: CRM/Sepa/DAO/SEPASddFile.php +msgid "Created ID" +msgstr "Ersteller-ID" + #: CRM/Sepa/DAO/SEPASddFile.php msgid "FK to Contact ID of creator" msgstr "" +#: CRM/Sepa/DAO/SEPASddFile.php CRM/Sepa/DAO/SEPATransactionGroup.php +msgid "Status ID" +msgstr "Status-ID" + #: CRM/Sepa/DAO/SEPASddFile.php msgid "fk to Batch Status options in civicrm_option_values" msgstr "" @@ -841,16 +876,12 @@ msgid "Tag used to group multiple creditors in this XML file." msgstr "" #: CRM/Sepa/DAO/SEPATransactionGroup.php -#, fuzzy -#| msgid "View SEPA transaction groups" msgid "SEPATransaction Groups" -msgstr "SEPA-Gruppen ansehen" +msgstr "SEPATransaction-Gruppen" #: CRM/Sepa/DAO/SEPATransactionGroup.php -#, fuzzy -#| msgid "View SEPA transaction groups" msgid "SEPATransaction Group" -msgstr "SEPA-Gruppen ansehen" +msgstr "SEPATransaction-Gruppe" #: CRM/Sepa/DAO/SEPATransactionGroup.php msgid "End-to-end reference for this tx group." @@ -869,21 +900,25 @@ msgid "Collection Date" msgstr "Einzugsdatum" #: CRM/Sepa/DAO/SEPATransactionGroup.php -#, fuzzy -#| msgid "Earliest Collection Date" msgid "Target collection date" -msgstr "Frühester Einzugstermin" +msgstr "Zieleinzugsdatum" #: CRM/Sepa/DAO/SEPATransactionGroup.php msgid "fk sepa group Status options in civicrm_option_values" msgstr "" #: CRM/Sepa/DAO/SEPATransactionGroup.php -#, fuzzy -#| msgid "Creditor ID" -msgid "fk to SDD Creditor Id" +msgid "Sdd Creditor ID" msgstr "Gläubiger-ID" +#: CRM/Sepa/DAO/SEPATransactionGroup.php +msgid "fk to SDD Creditor Id" +msgstr "Fremdschlüssel zu Gläubiger-ID" + +#: CRM/Sepa/DAO/SEPATransactionGroup.php +msgid "Sdd File ID" +msgstr "Sdd-Datei-ID" + #: CRM/Sepa/DAO/SEPATransactionGroup.php msgid "fk to SDD File Id" msgstr "" @@ -893,6 +928,8 @@ msgid "SepaMandate ID" msgstr "Mandats-ID" #: CRM/Sepa/DAO/SepaMandateLink.php CRM/Sepa/Form/Report/SepaMandateGeneric.php +#: Civi/Sepa/ActionProvider/Action/CreateOneOffMandate.php +#: Civi/Sepa/ActionProvider/Action/CreateRecurringMandate.php msgid "Creation Date" msgstr "Erstellungsdatum" @@ -914,6 +951,12 @@ msgstr "Startdatum" msgid "End Date" msgstr "Enddatum" +#: CRM/Sepa/Form/CreateMandate.php +msgid "" +"The mandate to clone/replace from does not exist or you do not have " +"permission for it." +msgstr "" + #: CRM/Sepa/Form/CreateMandate.php msgid "You can only replace RCUR mandates" msgstr "Sie können nur Mandate mit wiederkehrenden Zahlungen (RCUR) ersetzen" @@ -942,10 +985,8 @@ msgid "Creditor" msgstr "Kreditor" #: CRM/Sepa/Form/CreateMandate.php -#, fuzzy -#| msgid "Payment Details" msgid "Payment Method" -msgstr "Zahlungsdetails" +msgstr "Zahlungsmethode" #: CRM/Sepa/Form/CreateMandate.php CRM/Sepa/Form/Report/SepaMandateOOFF.php #: CRM/Sepa/Form/Report/SepaMandateRCUR.php @@ -975,15 +1016,20 @@ msgstr "nicht benötigt" msgid "Account" msgstr "Konto" +#: CRM/Sepa/Form/CreateMandate.php +msgid "not required if same as contact" +msgstr "nicht benötigt wenn gleich mit Kontakt" + #: CRM/Sepa/Form/CreateMandate.php msgid "required" msgstr "erforderlich" #: CRM/Sepa/Form/CreateMandate.php CRM/Sepa/Form/Report/SepaMandateGeneric.php #: CRM/Sepa/Form/Report/SepaMandateOOFF.php CRM/Sepa/Page/CreateMandate.php +#: CRM/Utils/SepaTokens.php #: Civi/Sepa/ActionProvider/Action/CreateOneOffMandate.php #: Civi/Sepa/ActionProvider/Action/CreateRecurringMandate.php -#: Civi/Sepa/ActionProvider/Action/FindMandate.php sepa.php +#: Civi/Sepa/ActionProvider/Action/FindMandate.php #: templates/CRM/Contribute/Form/ContributionThankYou.sepa.tpl #: templates/CRM/Event/Form/RegistrationThankYou.sepa.tpl #: templates/CRM/Sepa/Page/CreateMandate.tpl @@ -1077,6 +1123,7 @@ msgstr "Generic SEPA Mandate Report (org.project60.sepa)" #: Civi/Sepa/ActionProvider/Action/CreateOneOffMandate.php #: Civi/Sepa/ActionProvider/Action/CreateRecurringMandate.php #: Civi/Sepa/ActionProvider/Action/FindMandate.php +#: Civi/Sepa/ActionProvider/Action/TerminateMandate.php #: templates/CRM/Contribute/Form/ContributionThankYou.sepa.tpl #: templates/CRM/Event/Form/RegistrationThankYou.sepa.tpl #: templates/CRM/Sepa/Page/CreateMandate.tpl @@ -1087,6 +1134,7 @@ msgstr "Mandatsreferenz" #: Civi/Sepa/ActionProvider/Action/CreateOneOffMandate.php #: Civi/Sepa/ActionProvider/Action/CreateRecurringMandate.php #: Civi/Sepa/ActionProvider/Action/FindMandate.php +#: Civi/Sepa/ActionProvider/Action/TerminateMandate.php msgid "Mandate ID" msgstr "Mandats-ID" @@ -1094,10 +1142,10 @@ msgstr "Mandats-ID" msgid "Mandate Status" msgstr "Status des Mandats" -#: CRM/Sepa/Form/Report/SepaMandateGeneric.php +#: CRM/Sepa/Form/Report/SepaMandateGeneric.php CRM/Utils/SepaTokens.php #: Civi/Sepa/ActionProvider/Action/CreateOneOffMandate.php #: Civi/Sepa/ActionProvider/Action/CreateRecurringMandate.php -#: Civi/Sepa/ActionProvider/Action/FindMandate.php sepa.php +#: Civi/Sepa/ActionProvider/Action/FindMandate.php msgid "Signature Date" msgstr "Unterschriftsdatum" @@ -1128,6 +1176,10 @@ msgstr "Einmaleinzug" msgid "Recurring" msgstr "Dauereinzug" +#: CRM/Sepa/Form/Report/SepaMandateGeneric.php +msgid "account_holder" +msgstr "" + #: CRM/Sepa/Form/Report/SepaMandateGeneric.php msgid "Contact Name" msgstr "Kontaktname" @@ -1148,11 +1200,8 @@ msgstr "SEPA Mandate (Einzeleinzug)" msgid "SEPA One-Off Mandate Report (org.project60.sepa)" msgstr "SEPA Einzeleinzugs-Mandate (org.project60.sepa)" -#: CRM/Sepa/Form/Report/SepaMandateOOFF.php -msgid "Contribution ID" -msgstr "Zuwendungs-ID" - #: CRM/Sepa/Form/Report/SepaMandateOOFF.php CRM/Sepa/Form/RetryCollection.php +#: Civi/Sepa/ActionProvider/Action/TerminateMandate.php #: templates/CRM/Sepa/Page/EditMandate.tpl msgid "Cancel Reason" msgstr "Änderungsgrund" @@ -1190,7 +1239,7 @@ msgstr "SEPA Mandate (Dauereinzüge)" msgid "SEPA Recurring Mandate Report (org.project60.sepa)" msgstr "SEPA Dauereinzugs-Mandate (org.project60.sepa)" -#: CRM/Sepa/Form/Report/SepaMandateRCUR.php sepa.php +#: CRM/Sepa/Form/Report/SepaMandateRCUR.php CRM/Utils/SepaTokens.php msgid "Cycle Day" msgstr "Monatstag" @@ -1251,10 +1300,8 @@ msgid "SDD Groups" msgstr "SEPA-Gruppen" #: CRM/Sepa/Form/RetryCollection.php -#, fuzzy -#| msgid "Transaction Message" msgid "Custom Transaction Message" -msgstr "Verwendungszweck" +msgstr "Benutzerdefinierte Transaktionsnachricht" #: CRM/Sepa/Form/RetryCollection.php templates/CRM/Sepa/Page/CreateMandate.tpl msgid "Note" @@ -1373,6 +1420,52 @@ msgstr "Dateiformat [%1] nicht vorhanden!" msgid "Cannot close transaction group! Error was: '%s'" msgstr "Die Gruppe konnte nicht geschlossen werden. Fehler ist: '%s'" +#: CRM/Sepa/Logic/MandateRepairs.php +msgid "" +"%1 orphaned open (pending) SEPA contributions were found in the system, i.e. " +"they are not part of a SEPA transaction group, and will not be collected any " +"more. You should delete them by searching for contributions in status " +"'Pending' with payment instruments RCUR and FRST." +msgstr "" + +#: CRM/Sepa/Logic/MandateRepairs.php +msgid "" +"WARNING: %1 orphaned active (in Progress) SEPA contributions detected. These " +"may cause irregularities in the generation of the SEPA collection groups, " +"and in particular might cause the same installment to be collected multiple " +"times. You should find them by searching for contributions in status 'in " +"Progress' with the SEPA payment instruments (e.g. RCUR and FRST), and then " +"export (to be safe) and delete them." +msgstr "" + +#: CRM/Sepa/Logic/MandateRepairs.php +msgid "" +"Warning: had to adjusted the status of %1 contribution(s) to 'Pending', as " +"they are part of an open transaction group." +msgstr "" + +#: CRM/Sepa/Logic/MandateRepairs.php +msgid "Adjusted the payment instruments of %1 recurring mandate(s)." +msgstr "Zahlungsmethode von %1 wiederkehrenden Mandaten wurden angepasst." + +#: CRM/Sepa/Logic/MandateRepairs.php +msgid "" +"The following irregularities have been detected and fixed in your database:" +msgstr "" +"Die folgenden Unregelmäßigkeiten wurden erkannt und in der Datenbank behoben:" + +#: CRM/Sepa/Logic/MandateRepairs.php +msgid "You can find a detailed log of the changes here: %1" +msgstr "" + +#: CRM/Sepa/Logic/MandateRepairs.php +msgid "CiviSEPA Health Check" +msgstr "CiviSEPA-Selbstkontrolle" + +#: CRM/Sepa/Logic/Queue/Close.php +msgid "Marking SDD Group(s) Received: [%1]" +msgstr "Markiere SDD-Gruppe(n) als erhalten: [%1]" + #: CRM/Sepa/Logic/Queue/Close.php msgid "Closing SDD Group(s) [%1]" msgstr "Schließe SEPA-Gruppe(n) [%1]" @@ -1406,6 +1499,14 @@ msgstr "Bereinigung beendeter Mandate wird vorbereitet" msgid "Cleaning up ended mandates" msgstr "Beendet Mandate werden bereinigt" +#: CRM/Sepa/Logic/Queue/Update.php +msgid "Process %1 mandates (%2-%3)" +msgstr "" + +#: CRM/Sepa/Logic/Queue/Update.php +msgid "Cleaning up %1 groups" +msgstr "" + #: CRM/Sepa/Logic/Queue/Update.php msgid "Lock released" msgstr "Sperre aufgehoben" @@ -1414,6 +1515,10 @@ msgstr "Sperre aufgehoben" msgid "Thank you" msgstr "Vielen Dank" +#: CRM/Sepa/Logic/Settings.php +msgid "In Progress" +msgstr "" + #: CRM/Sepa/Logic/Status.php msgid "Not activated" msgstr "Nicht aktiv" @@ -1575,8 +1680,8 @@ msgid "CiviSEPA Dashboard" msgstr "CiviSEPA Dashboard" #: CRM/Sepa/Page/DashBoard.php -msgid "Couldn't read transaction groups. Error was: '%s'" -msgstr "Die Gruppen konnten nicht geladen werden. Fehler ist: '%s'" +msgid "Couldn't read transaction groups. Error was: %1" +msgstr "Die Gruppen konnten nicht geladen werden. Fehler war: %1" #: CRM/Sepa/Page/DashBoard.php msgid "Unknown batcher mode '%s'. No batching triggered." @@ -1683,13 +1788,30 @@ msgid "SEPA Group Contributions" msgstr "Zuwendungen der SEPA Gruppe" #: CRM/Sepa/Page/ListGroup.php -msgid "Cannot read SEPA transaction group [%s]. Error was: '%s'" -msgstr "Die SEPA Gruppe [%s] konnte nicht geladen werden. Fehler ist: '%s'" +msgid "Cannot read SEPA transaction group [%1]. Error was: %2" +msgstr "" +"Die SEPA-Transaktionsgruppe [%1] konnte nicht geladen werden. Fehler: %2" #: CRM/Sepa/Page/MandateTab.php sepa.php msgid "SEPA Mandates" msgstr "SEPA Mandate" +#: CRM/Sepa/Page/MarkGroupReceived.php +msgid "Mark SEPA group received" +msgstr "SEPA-Gruppen als erhalten markieren" + +#: CRM/Sepa/Page/MarkGroupReceived.php templates/CRM/Sepa/Page/DeleteGroup.tpl +msgid "No group_id given!" +msgstr "Keine Gruppen-ID (group_id) angegeben." + +#: CRM/Sepa/Page/MarkGroupReceived.php +msgid "Cannot mark TEST groups as received." +msgstr "" + +#: CRM/Sepa/Page/MarkGroupReceived.php +msgid "Couldn't close SDD group #%1.
Error was: %2" +msgstr "SDD-Gruppe #%1 konnte nicht geschlossen werden. Fehler: %2" + #: CRM/Sepa/Page/SepaFile.php msgid "Generate XML File" msgstr "Erzeuge XML-Datei" @@ -1714,10 +1836,6 @@ msgstr "SEPA Standardnachrichtenvorlage PDF" msgid "SEPA Direct Debit Payment Information" msgstr "SEPA Zahlungsinformationen" -#: CRM/Sepa/Upgrader/Base.php -msgid "Upgrade %1 to revision %2" -msgstr "" - #: CRM/Sepa/Upgrader.php msgid "" "Your CiviSEPA payment processors have been disabled, the code was moved into " @@ -1733,10 +1851,8 @@ msgstr "" "releases\">CiviSEPA Payment Processor Erweiterung." #: CRM/Sepa/Upgrader.php -#, fuzzy -#| msgid "Payment Processor Settings" msgid "%1 Payment Processor(s) Disabled!" -msgstr "Einstellungen Zahlungsprozessor" +msgstr "%1 Zahlungsprzessor(en) deaktiviert!" #: CRM/Sepa/Upgrader.php msgid "" @@ -1767,6 +1883,40 @@ msgstr "unregelmäßig" msgid "SEPA Standard (FRST/RCUR)" msgstr "" +#: CRM/Utils/SepaTokens.php +msgid "Signature Date (raw)" +msgstr "Unterschriftsdatum (unformatiert)" + +#: CRM/Utils/SepaTokens.php +msgid "IBAN (anonymised)" +msgstr "IBAN (anonymisiert)" + +#: CRM/Utils/SepaTokens.php +msgid "Amount (raw)" +msgstr "Betrag (unformatiert)" + +#: CRM/Utils/SepaTokens.php +msgid "First Collection Date (raw)" +msgstr "Datum des ersten Einzugs (unformatiert)" + +#: CRM/Utils/SepaTokens.php +#: templates/CRM/Contribute/Form/ContributionThankYou.sepa.tpl +#: templates/CRM/Event/Form/RegistrationThankYou.sepa.tpl +msgid "First Collection Date" +msgstr "Erster Einzugstermin" + +#: CRM/Utils/SepaTokens.php +msgid "Interval Multiplier" +msgstr "Intervallänge" + +#: CRM/Utils/SepaTokens.php +msgid "Interval Unit" +msgstr "Intervalleinheit" + +#: CRM/Utils/SepaTokens.php templates/CRM/Sepa/Page/CreateMandate.tpl +msgid "Interval" +msgstr "Turnus" + #: Civi/Sepa/ActionProvider/Action/CreateOneOffMandate.php #: Civi/Sepa/ActionProvider/Action/CreateRecurringMandate.php msgid "Creditor (default)" @@ -1809,6 +1959,22 @@ msgstr "Einzugstag (Standard)" msgid "Collection Day" msgstr "Einzugstag" +#: Civi/Sepa/ActionProvider/Action/CreateRecurringMandate.php +msgid "Creditor (Leave empty to use default)" +msgstr "Gläubiger (Leer lassen für Standardwert)" + +#: Civi/Sepa/ActionProvider/Action/CreateRecurringMandate.php +msgid "Financial Type (Leave empty to use default)" +msgstr "Zuwendungsart (Leer lassen für Standardwert)" + +#: Civi/Sepa/ActionProvider/Action/CreateRecurringMandate.php +msgid "Campaign (Leave empty to use default)" +msgstr "Kampagne (Leer lassen für Standardwert)" + +#: Civi/Sepa/ActionProvider/Action/CreateRecurringMandate.php +msgid "Recurring Contribution ID" +msgstr "ID der wiederkehrenden Zuwendung" + #: Civi/Sepa/ActionProvider/Action/CreateRecurringMandate.php msgid "bi-monthly" msgstr "alle 2 Monate" @@ -1818,8 +1984,6 @@ msgid "as soon as possible" msgstr "sobald wie möglich" #: Civi/Sepa/ActionProvider/Action/FindMandate.php -#, fuzzy -#| msgid "One-off" msgid "One-Off" msgstr "Einmaleinzug" @@ -1857,6 +2021,10 @@ msgstr "Auswahl (falls mehrere gefunden)" msgid "Annual Amount" msgstr "Jahresbetrag" +#: Civi/Sepa/ActionProvider/Action/TerminateMandate.php +msgid "Cancel Reason (if no parameter)" +msgstr "Änderungsgrund (wenn keine Parameter)" + #: Civi/Sepa/ContainerSpecs.php msgid "Create SEPA Mandate (One-Off)" msgstr "SEPA Mandat (Einzel-Lastschrift) erstellen" @@ -1869,6 +2037,55 @@ msgstr "SEPA Mandat (Dauerlastschrift) erstellen" msgid "Find SEPA Mandate" msgstr "SEPA-Mandate Finden" +#: Civi/Sepa/ContainerSpecs.php +msgid "Terminate SEPA Mandate" +msgstr "SEPA-Mandat beenden" + +#: Civi/Sepa/ContainerSpecs.php +#: templates/CRM/Contact/Page/View/Summary.sepa.tpl +msgid "SEPA Mandate" +msgstr "SEPA-Mandat" + +#: Civi/Sepa/ContainerSpecs.php +msgid "SEPA Creditor" +msgstr "SEPA-Gläubiger" + +#: Civi/Sepa/ContainerSpecs.php +msgid "SEPA Transaction Group" +msgstr "SEPA-Transaktionsgruppe" + +#: Civi/Sepa/ContainerSpecs.php +msgid "SEPA SDD File" +msgstr "SEPA-SDD-Datei" + +#: Civi/Sepa/ContainerSpecs.php +msgid "SEPA Contribution Group" +msgstr "SEPA-Zuwendungsgruppe" + +#: Civi/Sepa/ContainerSpecs.php +msgid "SEPA Mandate Link" +msgstr "SEPA-Mandatsverweis" + +#: Civi/Sepa/ContainerSpecs.php +msgid "Join Sepa Mandate on Contribution" +msgstr "SEPA-Mandat mit Zuwendung verbinden (join)" + +#: Civi/Sepa/ContainerSpecs.php +msgid "Join Sepa Mandate on Contribution Recur" +msgstr "SEPA-Mandat mit wiederkehrender Zuwendung verbinden (join)" + +#: Civi/Sepa/DataProcessor/Join/AbstractMandateJoin.php +msgid "Select field" +msgstr "" + +#: Civi/Sepa/DataProcessor/Join/AbstractMandateJoin.php +msgid "Required" +msgstr "Erforderlich" + +#: Civi/Sepa/DataProcessor/Join/AbstractMandateJoin.php +msgid "Not required" +msgstr "Nicht erforderlich" + #: api/v3/SepaTransactionGroup.php msgid "SEPA DD Transaction Batch" msgstr "SEPA DD Einzugsgruppe" @@ -1945,10 +2162,8 @@ msgid "Error" msgstr "Fehler" #: sepa.php -#, fuzzy -#| msgid "Recurring SEPA Mandates" msgid "Record SEPA Mandate" -msgstr "Wiederkehrende SEPA-Mandate" +msgstr "SEPA-Mandat erfassen" #: sepa.php msgid "CiviSEPA Settings" @@ -1962,30 +2177,58 @@ msgstr "CiviSEPA" msgid "Create SEPA mandates" msgstr "Lege SEPA-Mandate an" +#: sepa.php +msgid "Allows creating SEPA Direct Debit mandates." +msgstr "" + #: sepa.php msgid "View SEPA mandates" -msgstr "SEPA-Mandtae ansehen" +msgstr "SEPA-Mandate ansehen" + +#: sepa.php +msgid "Allows viewing SEPA Direct Debit mandates" +msgstr "" #: sepa.php msgid "Edit SEPA mandates" msgstr "SEPA-Mandate bearbeiten" +#: sepa.php +msgid "Allows editing SEPA Direct Debit mandates." +msgstr "" + #: sepa.php msgid "Delete SEPA mandates" msgstr "SEPA-Mandate löschen" +#: sepa.php +msgid "Allows deleting SEPA Direct Debit mandates" +msgstr "" + #: sepa.php msgid "View SEPA transaction groups" msgstr "SEPA-Gruppen ansehen" +#: sepa.php +msgid "Allows viewing groups of SEPA transactions to be sent to the bank." +msgstr "" + #: sepa.php msgid "Batch SEPA transaction groups" msgstr "SEPA-Gruppen erzeugen" +#: sepa.php +msgid "Allows generating groups of SEPA transactions to be sent to the bank." +msgstr "" + #: sepa.php msgid "Delete SEPA transaction groups" msgstr "SEPA-Gruppen löschen" +#: sepa.php +msgid "Allows deleting groups of SEPA transactions to be sent to the bank." +msgstr "" + #: sepa.php msgid "" "This contribution has no mandate and cannot simply be changed to a SEPA " @@ -1998,39 +2241,6 @@ msgstr "" msgid "Most Recent SEPA Mandate" msgstr "Letztes SEPA Mandat" -#: sepa.php -msgid "Signature Date (raw)" -msgstr "Unterschriftsdatum (unformatiert)" - -#: sepa.php -msgid "IBAN (anonymised)" -msgstr "IBAN (anonymisiert)" - -#: sepa.php -msgid "Amount (raw)" -msgstr "Betrag (unformatiert)" - -#: sepa.php -msgid "First Collection Date (raw)" -msgstr "Datum des ersten Einzugs (unformatiert)" - -#: sepa.php templates/CRM/Contribute/Form/ContributionThankYou.sepa.tpl -#: templates/CRM/Event/Form/RegistrationThankYou.sepa.tpl -msgid "First Collection Date" -msgstr "Erster Einzugstermin" - -#: sepa.php -msgid "Interval Multiplier" -msgstr "Intervallänge" - -#: sepa.php -msgid "Interval Unit" -msgstr "Intervalleinheit" - -#: sepa.php templates/CRM/Sepa/Page/CreateMandate.tpl -msgid "Interval" -msgstr "Turnus" - #: templates/CRM/Admin/Form/Setting/SepaSettings.hlp msgid "" "This is the name of the contact to which this creditor is associated to." @@ -2301,12 +2511,12 @@ msgstr "" #: templates/CRM/Admin/Form/Setting/SepaSettings.hlp msgid "" -"If you have the Little BIC Extension installed, the BIC can be derived automatically " -"from the IBAN in most cases." +"If you have the Little BIC Extension installed, the BIC can be derived " +"automatically from the IBAN in most cases." msgstr "" -"Wenn Sie die Little BIC Extension installiert haben, kann die BIC automatisch " +"Wenn Sie die Little BIC Extension installiert haben, kann die BIC automatisch " "anhand der IBAN ermittelt werden." #: templates/CRM/Admin/Form/Setting/SepaSettings.hlp @@ -2460,6 +2670,12 @@ msgid "" "If you leave this empty, no (new) one-off mandates can be created any more." msgstr "" +#: templates/CRM/Admin/Form/Setting/SepaSettings.hlp +msgid "" +"CUC-code (\"Codice Univoco CBI\") of the financial institution of the " +"creditor. It is only required for CBIBdySDDReq SEPA file format." +msgstr "" + #: templates/CRM/Admin/Form/Setting/SepaSettings.tpl msgid "Creditors" msgstr "Einzugsberechtigte" @@ -2561,10 +2777,6 @@ msgstr "Keine vorläufigen XML-Dateien" msgid "Support Large Groups" msgstr "Unterstützung für große SEPA-Gruppen" -#: templates/CRM/Contact/Page/View/Summary.sepa.tpl -msgid "SEPA Mandate" -msgstr "SEPA-Mandat" - #: templates/CRM/Contact/Page/View/Summary.sepa.tpl #: templates/CRM/Sepa/Page/DashBoard.tpl templates/CRM/Sepa/Page/ListGroup.tpl msgid "Contributions" @@ -2584,10 +2796,6 @@ msgstr "Ich möchte diesen Betrag %1 zahlen." msgid "This payment will be debited from the following account:" msgstr "Die Zahlung wird von dem folgenden Bankkonto abgebucht:" -#: templates/CRM/Contribute/Form/ContributionConfirm.sepa.tpl -msgid "Account Holder" -msgstr "Kontoinhaber" - #: templates/CRM/Contribute/Form/ContributionConfirm.sepa.tpl msgid "Bank Name" msgstr "Bankname" @@ -2646,6 +2854,45 @@ msgstr "Frühester Einzugstermin" msgid "You're replacing mandate %1" msgstr "Sie ersetzen Mandat %1" +#: templates/CRM/Sepa/Form/DataProcessor/Join/MandateContributionJoin.tpl +msgid "" +"Select the ID of the contribution. This could be either on the contribution " +"source with the field id. Or the any other data source which holds a " +"contribution ID field." +msgstr "" + +#: templates/CRM/Sepa/Form/DataProcessor/Join/MandateContributionJoin.tpl +#: templates/CRM/Sepa/Form/DataProcessor/Join/MandateContributionRecurJoin.tpl +msgid "Required join" +msgstr "erforderlicher Join" + +#: templates/CRM/Sepa/Form/DataProcessor/Join/MandateContributionJoin.tpl +msgid "" +"Required means that both Sepa Mandate Entity ID field and the Contribution " +"ID need to be set. " +msgstr "" + +#: templates/CRM/Sepa/Form/DataProcessor/Join/MandateContributionJoin.tpl +msgid "Join on Contribution ID field" +msgstr "Join auf ID-Feld der Zuwendung" + +#: templates/CRM/Sepa/Form/DataProcessor/Join/MandateContributionRecurJoin.tpl +msgid "" +"Select the ID of the contribution recur. This could be either on the " +"contribution recur source with the field id. Or the contribution source and " +"field contribution_recur_id." +msgstr "" + +#: templates/CRM/Sepa/Form/DataProcessor/Join/MandateContributionRecurJoin.tpl +msgid "" +"Required means that both Sepa Mandate Entity ID field and the Recurring " +"Contribution ID need to be set. " +msgstr "" + +#: templates/CRM/Sepa/Form/DataProcessor/Join/MandateContributionRecurJoin.tpl +msgid "Join on Contribution Recur ID field" +msgstr "Join auf ID-Feld der wiederkehrenden Zuwendung" + #: templates/CRM/Sepa/Form/RetryCollection.hlp msgid "" "Allows you to add a customised transaction message, i.e. the message the " @@ -2659,12 +2906,12 @@ msgid "" msgstr "" #: templates/CRM/Sepa/Form/RetryCollection.tpl -#, fuzzy -#| msgid "Select the transaction groups you want to re-collect." msgid "" "Select the transaction groups of which you want to re-collect failed " "transactions" -msgstr "Wählen Sie die SEPA-Gruppen aus, die Sie erneut einziehen möchten." +msgstr "" +"Wählen Sie die SEPA-Gruppen aus, für die Sie fehlgeschlagene Transaktionen " +"erneut einziehen möchten." #: templates/CRM/Sepa/Form/RetryCollection.tpl msgid "Add some filters" @@ -2842,12 +3089,6 @@ msgstr "Jetzt noch nicht" msgid "It's very important to select one of the options below." msgstr "Bitte unbedingt eine der Optionen unten auswählen." -#: templates/CRM/Sepa/Page/CreateMandate.tpl -#: templates/CRM/Sepa/Page/EditMandate.tpl -#: templates/CRM/Sepa/Page/ListGroup.tpl -msgid "Contact" -msgstr "Kontakt" - #: templates/CRM/Sepa/Page/CreateMandate.tpl msgid "Replacing Mandate" msgstr "Mandat ersetzen" @@ -2908,6 +3149,14 @@ msgstr "Dauereinzüge Aktualisieren" msgid "retry collection" msgstr "Einzug erneut versuchen" +#: templates/CRM/Sepa/Page/DashBoard.tpl +msgid "" +"Note that only groups with contributions of authorized financial types are " +"being displayed." +msgstr "" +"Beachten Sie, dass nur Gruppen mit Zuwendungen angezeigt werden, für deren " +"Zuwendungsart Sie die Berechtigung haben." + #: templates/CRM/Sepa/Page/DashBoard.tpl msgid "Group Name" msgstr "Gruppenname" @@ -2929,10 +3178,8 @@ msgid "Total" msgstr "Gesamt" #: templates/CRM/Sepa/Page/DashBoard.tpl -#, fuzzy -#| msgid "Transaction Message" msgid "Custom Transaction Message:" -msgstr "Verwendungszweck" +msgstr "Benutzerdefinierte Transaktionsnachricht:" #: templates/CRM/Sepa/Page/DashBoard.tpl msgid "Note:" @@ -3109,6 +3356,10 @@ msgstr "alle %1 löschen" msgid "delete pending %1" msgstr "ausstehende %1 löschen" +#: templates/CRM/Sepa/Page/DeleteGroup.tpl +msgid "recommended" +msgstr "" + #: templates/CRM/Sepa/Page/DeleteGroup.tpl msgid "" "You should consider deleting the open contributions. If the recurring " @@ -3310,10 +3561,6 @@ msgstr "SEPA Gruppe '%1' wurde erfolgreich gelöscht." msgid "Back" msgstr "Zurück" -#: templates/CRM/Sepa/Page/DeleteGroup.tpl -msgid "No group_id given!" -msgstr "Keine Gruppen-ID (group_id) angegeben." - #: templates/CRM/Sepa/Page/DeleteGroup.tpl msgid "Transaction group [%1] couldn't be loaded." msgstr "SEPA Gruppe [%1] konnte nicht geladen werden." @@ -3338,6 +3585,12 @@ msgid "" msgstr "" "Alle Nachrichtenvorlagen die mit 'SEPA' beginnen, werden hier angezeigt." +#: templates/CRM/Sepa/Page/EditMandate.hlp +msgid "" +"When you change the cycle day be aware that you might end up collecting " +"twice in the same month, or miss a collection altogether." +msgstr "" + #: templates/CRM/Sepa/Page/EditMandate.tpl msgid "SEPA Recurring Mandate" msgstr "SEPA Dauereinzugs-Mandat" @@ -3434,6 +3687,18 @@ msgstr "Mandat ändern zum:" msgid "Replace for the following reason:" msgstr "Grund der Änderung:" +#: templates/CRM/Sepa/Page/EditMandate.tpl +msgid "Change Cycle Day" +msgstr "Einzugstag ändern" + +#: templates/CRM/Sepa/Page/EditMandate.tpl +msgid "New cycle day:" +msgstr "Neuer Einzugstag:" + +#: templates/CRM/Sepa/Page/EditMandate.tpl +msgid "Cyle Day" +msgstr "Einzugstag" + #: templates/CRM/Sepa/Page/EditMandate.tpl msgid "Adjust Amount" msgstr "Betrag anpassen" @@ -3542,6 +3807,14 @@ msgstr "Mandat bearbeiten" msgid "This contact has no recorded recurring mandates." msgstr "Dieser Kontakt hat keine Mandate mit wiederkehrenden Zahlungen." +#: templates/CRM/Sepa/Page/MandateTab.tpl +msgid "" +"Note that only mandates associated with contributions of authorized " +"financial types are being displayed." +msgstr "" +"Beachten Sie, dass nur Mandate mit zugehörigen Zuwendungen angezeigt werden, " +"für deren Zuwendungsart Sie die Berechtigung haben." + #: templates/CRM/Sepa/Page/MandateTab.tpl msgid "One-Off SEPA Mandates" msgstr "Einmalige SEPA-Latschriften" @@ -3584,6 +3857,17 @@ msgstr "Fertig" msgid "Notes" msgstr "Notizen" +#: tests/phpunit/CRM/Sepa/BugReproductionTest.php +#: tests/phpunit/CRM/Sepa/MandateTest.php +msgid "OOFF Mandate status after closing is incorrect." +msgstr "" + +#: tests/phpunit/CRM/Sepa/BugReproductionTest.php +msgid "" +"OOFF contribution status after closing is incorrect, probably related to " +"SEPA-629" +msgstr "" + #: tests/phpunit/CRM/Sepa/HookTest.php msgid "The create_mandate hook has not been called." msgstr "" @@ -3690,10 +3974,6 @@ msgstr "" msgid "RCUR transaction group status after batching is incorrect." msgstr "" -#: tests/phpunit/CRM/Sepa/MandateTest.php -msgid "OOFF Mandate status after closing is incorrect." -msgstr "" - #: tests/phpunit/CRM/Sepa/MandateTest.php msgid "OOFF contribution status after closing is incorrect." msgstr "" @@ -3718,16 +3998,16 @@ msgstr "" msgid "Mandate status of new mandate is incorrect." msgstr "" -#: tests/phpunit/CRM/Sepa/ReferenceGenerationTest.php -msgid "The OOFF mandate reference is invalid." +#: tests/phpunit/CRM/Sepa/MandateTest.php +msgid "Mandate account holder after creation is incorrect." msgstr "" #: tests/phpunit/CRM/Sepa/ReferenceGenerationTest.php -msgid "The RCUR mandate reference is invalid." +msgid "The OOFF mandate reference is invalid." msgstr "" #: tests/phpunit/CRM/Sepa/ReferenceGenerationTest.php -msgid "There should be a clashing reference" +msgid "The RCUR mandate reference is invalid." msgstr "" #: tests/phpunit/CRM/Sepa/SettingsTest.php @@ -3739,9 +4019,7 @@ msgid "set/getSetting doesn't work" msgstr "" #: tests/phpunit/CRM/Sepa/TestBase.php -msgid "" -"The contribution for the mandate is null. That should not be possible at " -"this point." +msgid "This mandate has no contribution, even though there should be one." msgstr "" #: tests/phpunit/CRM/Sepa/VerifyBicTest.php @@ -3784,6 +4062,9 @@ msgstr "" msgid "Blocklistet IBAN should fail but did not!" msgstr "" +#~ msgid "Couldn't read transaction groups. Error was: '%s'" +#~ msgstr "Die Gruppen konnten nicht geladen werden. Fehler ist: '%s'" + #~ msgid "Does this creditor use BICs?" #~ msgstr "Verwendet dieser Kreditor BICs?" @@ -3867,9 +4148,6 @@ msgstr "" #~ "Beendigungsgrund für Mandat [%s] konnte nicht gesetzt werden. Fehler ist: " #~ "'%s'" -#~ msgid "Couldn't create note for contribution #%s" -#~ msgstr "Konnte die Zuwendungsnotiz für [%s] nicht erstellen" - #~ msgid "edit mandate" #~ msgstr "Mandat Bearbeiten" diff --git a/l10n/org.project60.sepa.pot b/l10n/org.project60.sepa.pot index a375ecbe..84a6e4c5 100644 --- a/l10n/org.project60.sepa.pot +++ b/l10n/org.project60.sepa.pot @@ -178,7 +178,7 @@ msgstr "" msgid "Cannot close mandate [%s], batching in progress!" msgstr "" -#: CRM/Sepa/BAO/SEPAMandate.php CRM/Sepa/Form/CreateMandate.php CRM/Sepa/Logic/Queue/Update.php CRM/Sepa/Logic/Status.php CRM/Sepa/Page/CloseGroup.php CRM/Sepa/Page/CreateMandate.php CRM/Sepa/Page/DashBoard.php CRM/Sepa/Page/EditMandate.php CRM/Sepa/Page/ListGroup.php js/SepaSettings.js +#: CRM/Sepa/BAO/SEPAMandate.php CRM/Sepa/Form/CreateMandate.php CRM/Sepa/Logic/Queue/Update.php CRM/Sepa/Logic/Status.php CRM/Sepa/Page/CloseGroup.php CRM/Sepa/Page/CreateMandate.php CRM/Sepa/Page/DashBoard.php CRM/Sepa/Page/EditMandate.php js/SepaSettings.js msgid "Error" msgstr "" @@ -782,6 +782,10 @@ msgstr "" msgid "End Date" msgstr "" +#: CRM/Sepa/Form/CreateMandate.php +msgid "The mandate to clone/replace from does not exist or you do not have permission for it." +msgstr "" + #: CRM/Sepa/Form/CreateMandate.php msgid "You can only replace RCUR mandates" msgstr "" @@ -1415,7 +1419,7 @@ msgid "CiviSEPA Dashboard" msgstr "" #: CRM/Sepa/Page/DashBoard.php -msgid "Couldn't read transaction groups. Error was: '%s'" +msgid "Couldn't read transaction groups. Error was: %1" msgstr "" #: CRM/Sepa/Page/DashBoard.php @@ -1515,7 +1519,7 @@ msgid "SEPA Group Contributions" msgstr "" #: CRM/Sepa/Page/ListGroup.php -msgid "Cannot read SEPA transaction group [%s]. Error was: '%s'" +msgid "Cannot read SEPA transaction group [%1]. Error was: %2" msgstr "" #: CRM/Sepa/Page/MandateTab.php sepa.php @@ -2546,6 +2550,10 @@ msgstr "" msgid "retry collection" msgstr "" +#: templates/CRM/Sepa/Page/DashBoard.tpl +msgid "Note that only groups with contributions of authorized financial types are being displayed." +msgstr "" + #: templates/CRM/Sepa/Page/DashBoard.tpl msgid "Group Name" msgstr "" @@ -3050,6 +3058,10 @@ msgstr "" msgid "This contact has no recorded recurring mandates." msgstr "" +#: templates/CRM/Sepa/Page/MandateTab.tpl +msgid "Note that only mandates associated with contributions of authorized financial types are being displayed." +msgstr "" + #: templates/CRM/Sepa/Page/MandateTab.tpl msgid "One-Off SEPA Mandates" msgstr "" diff --git a/org.project60.sepacustom/sepacustom.php b/org.project60.sepacustom/sepacustom.php index 3cf31f10..789d8e52 100644 --- a/org.project60.sepacustom/sepacustom.php +++ b/org.project60.sepacustom/sepacustom.php @@ -115,8 +115,13 @@ function sepacustom_civicrm_create_mandate(&$mandate_parameters) { for ($n=0; $n < 10; $n++) { $reference_candidate = sprintf($reference, $n); // check if it exists - $mandate = civicrm_api('SepaMandate', 'getsingle', array('version' => 3, 'reference' => $reference_candidate)); - if (isset($mandate['is_error']) && $mandate['is_error']) { + try { + $mandate = \Civi\Api4\SepaMandate::get(TRUE) + ->addWhere('reference', '=', $reference_candidate) + ->execute() + ->single(); + } + catch (\CRM_Core_Exception $exception) { // does not exist! take it! $mandate_parameters['reference'] = $reference_candidate; return; diff --git a/sepa.php b/sepa.php index 643dc7b6..fd5f1fce 100644 --- a/sepa.php +++ b/sepa.php @@ -46,7 +46,8 @@ function sepa_civicrm_pageRun( &$page ) { ]); } - } elseif (get_class($page) == "CRM_Contribute_Page_Tab") { + } + elseif (get_class($page) == "CRM_Contribute_Page_Tab") { // single contribuion view if (CRM_Core_Permission::check('view sepa mandates')) { $contribution_id = $page->getTemplate()->get_template_vars('id'); @@ -59,34 +60,40 @@ function sepa_civicrm_pageRun( &$page ) { $contribution_recur_id = $page->getTemplate()->get_template_vars('contribution_recur_id'); if (empty($contribution_recur_id)) return; - $mandate = civicrm_api3('SepaMandate', 'getsingle', [ - 'entity_table' => 'civicrm_contribution_recur', - 'entity_id' => $contribution_recur_id - ]); + $mandate = \Civi\Api4\SepaMandate::get(TRUE) + ->addWhere('entity_table', '=', 'civicrm_contribution_recur') + ->addWhere('entity_id', '=', $contribution_recur_id) + ->execute() + ->single(); } else { // this is a OOFF contribtion - $mandate = civicrm_api3('SepaMandate', 'getsingle', [ - 'entity_table' => 'civicrm_contribution', - 'entity_id' => $contribution_id - ]); + $mandate = \Civi\Api4\SepaMandate::get(TRUE) + ->addWhere('entity_table', '=', 'civicrm_contribution') + ->addWhere('entity_id', '=', $contribution_id) + ->execute() + ->single(); } // add txgroup information - $txgroup_search = civicrm_api3('SepaContributionGroup', 'get', [ - 'contribution_id' => $contribution_id - ]); - if (empty($txgroup_search['id'])) { + $txgroup_search = \Civi\Api4\SepaContributionGroup::get(TRUE) + ->addWhere('contribution_id', '=', $contribution_id) + ->execute() + ->indexBy('id'); + if ($txgroup_search->count() === 0) { $mandate['tx_group'] = ts('None', ['domain' => 'org.project60.sepa']); - } else { - $group = reset($txgroup_search['values']); + } + else { + $group = $txgroup_search->first(); if (empty($group['txgroup_id'])) { $mandate['tx_group'] = ts('Error', ['domain' => 'org.project60.sepa']); - } else { - $mandate['tx_group'] = civicrm_api3('SepaTransactionGroup', 'getvalue', [ - 'return' => 'reference', - 'id' => $group['txgroup_id'] - ]); + } + else { + $mandate['tx_group'] = \Civi\Api4\SepaTransactionGroup::get(TRUE) + ->addSelect('reference') + ->addWhere('id', '=', $group['txgroup_id']) + ->execute() + ->single()['reference']; } } @@ -112,7 +119,10 @@ function sepa_civicrm_pageRun( &$page ) { // this is not a SEPA recurring contribution return; } - $mandate = civicrm_api3("SepaMandate","getsingle", ['id' => $mandate_id]); + $mandate = \Civi\Api4\SepaMandate::get(TRUE) + ->addWhere('id', '=', $mandate_id) + ->execute() + ->single(); // load notes $mandate['notes'] = []; diff --git a/settings/sepa.setting.php b/settings/sepa.setting.php index ee162d11..40e3eef7 100644 --- a/settings/sepa.setting.php +++ b/settings/sepa.setting.php @@ -273,6 +273,17 @@ 'is_contact' => 0, 'description' => 'Enables asychronous batching', ), + 'sdd_financial_type_grouping' => array( + 'group_name' => 'SEPA Direct Debit Preferences', + 'group' => 'org.project60', + 'name' => 'sdd_financial_type_grouping', + 'type' => 'Boolean', + 'html_type' => 'checkbox', + 'default' => 0, + 'is_domain' => 1, + 'is_contact' => 0, + 'description' => 'Groups by Financial Types.', + ), 'sdd_skip_closed' => array( 'group_name' => 'SEPA Direct Debit Preferences', 'group' => 'org.project60', @@ -308,4 +319,4 @@ 'description' => "Contribution page buffer (in days) before debit needs to be collected", 'help_text' => "Contribution page buffer (in days) before debit needs to be collected", ) - ); \ No newline at end of file + ); diff --git a/sql/sepa.sql b/sql/sepa.sql index 00b184ef..17047d31 100644 --- a/sql/sepa.sql +++ b/sql/sepa.sql @@ -103,6 +103,7 @@ CREATE TABLE IF NOT EXISTS `civicrm_sdd_txgroup` ( `reference` varchar(64) COMMENT 'End-to-end reference for this tx group.', `type` char(4) COMMENT 'FRST, RCUR, OOFF or RTRY', `collection_date` datetime COMMENT 'Target collection date', + `financial_type_id` int unsigned COMMENT 'Financial type of contained contributions if CiviSEPA is generating groups matching financial types.', `latest_submission_date` datetime COMMENT 'Latest submission date', `created_date` datetime COMMENT 'When was this item created', `status_id` int unsigned NOT NULL COMMENT 'fk to Batch Status options in civicrm_option_values', diff --git a/templates/CRM/Admin/Form/Setting/SepaSettings.hlp b/templates/CRM/Admin/Form/Setting/SepaSettings.hlp index de0e645f..62463bdc 100644 --- a/templates/CRM/Admin/Form/Setting/SepaSettings.hlp +++ b/templates/CRM/Admin/Form/Setting/SepaSettings.hlp @@ -150,6 +150,11 @@

{ts}This option will activate a runner page with a progress bar for these critical processes. There shouldn't be any timeouts, but you have to keep that open until it's done.{/ts}

{/htxt} +{htxt id='id-financial-type-grouping'} +

{ts}Whether to group transactions by financial types additionally.{/ts}

+

{ts}Enable this settings if you use the Financial ACLs extension, as transaction groups with mixed financial type contributions can cause issues with overlapping Financial ACLs' permissions.{/ts}

+{/htxt} + {htxt id='id-creditor-type'}

{ts}Always use SEPA if you want to do SEPA style direct debit.{/ts}

{ts}PSP can help you to use this extension for other direct debit systems or payment service providers (PSP), but all SEPA style validations and tools are then disabled.{/ts}

@@ -178,4 +183,4 @@

{ts}CUC-code ("Codice Univoco CBI") of the financial institution of the creditor. It is only required for CBIBdySDDReq SEPA file format.{/ts}

{ts}Be careful when changing this!{/ts}

{/htxt} -{/crmScope} \ No newline at end of file +{/crmScope} diff --git a/templates/CRM/Admin/Form/Setting/SepaSettings.tpl b/templates/CRM/Admin/Form/Setting/SepaSettings.tpl index 0f355732..437f9c7b 100644 --- a/templates/CRM/Admin/Form/Setting/SepaSettings.tpl +++ b/templates/CRM/Admin/Form/Setting/SepaSettings.tpl @@ -338,6 +338,12 @@ div.sdd-add-creditor { {$form.sdd_async_batching.html} + + {$form.sdd_financial_type_grouping.label}   + + {$form.sdd_financial_type_grouping.html} + +
{include file="CRM/common/formButtons.tpl" location="bottom"}
diff --git a/templates/CRM/Sepa/Page/DashBoard.tpl b/templates/CRM/Sepa/Page/DashBoard.tpl index 9e5d1c42..f74f80dd 100644 --- a/templates/CRM/Sepa/Page/DashBoard.tpl +++ b/templates/CRM/Sepa/Page/DashBoard.tpl @@ -64,6 +64,12 @@
+{if $financialacls} +
+ {ts}Note that only groups with contributions of authorized financial types are being displayed.{/ts} +
+{/if} + diff --git a/templates/CRM/Sepa/Page/MandateTab.tpl b/templates/CRM/Sepa/Page/MandateTab.tpl index d5d60af3..451a53b4 100644 --- a/templates/CRM/Sepa/Page/MandateTab.tpl +++ b/templates/CRM/Sepa/Page/MandateTab.tpl @@ -21,11 +21,13 @@ {/literal} {* add new mandate button *} - +{if $permissions.create} + +{/if} {if $rcurs}

{ts domain="org.project60.sepa"}Recurring SEPA Mandates{/ts}

@@ -56,15 +58,20 @@
{ts domain="org.project60.sepa"}Group Name{/ts}{$rcur.total_amount|crmMoney:$rcur.currency} {$rcur.last_collection_date|crmDate:$date_format} - {foreach from=$rcur.fail_sequence item=fail} -
- {/foreach} + {* Show as many warnings as last installments have failed. *} + {if $rcur.fail_sequence} + {for $i=1 to $rcur.fail_sequence} +
+ {/for} + {/if}
{$rcur.next_collection_date|crmDate:$date_format} {$rcur.end_date|crmDate:$date_format} - {ts domain="org.project60.sepa"}View{/ts} - {if $rcur.edit_link} + {if $permissions.view} + {ts domain="org.project60.sepa"}View{/ts} + {/if} + {if $permissions.edit && $rcur.edit_link} {ts domain="org.project60.sepa"}Edit{/ts} {/if} @@ -76,6 +83,9 @@ {else}
{ts domain="org.project60.sepa"}This contact has no recorded recurring mandates.{/ts} +{if $financialacls} + {ts domain="org.project60.sepa"}Note that only mandates associated with contributions of authorized financial types are being displayed.{/ts} +{/if}
{/if} @@ -103,8 +113,10 @@
{$ooff.total_amount|crmMoney:$ooff.currency} - {ts domain="org.project60.sepa"}View{/ts} - {if $ooff.edit_link} + {if $permissions.view} + {ts domain="org.project60.sepa"}View{/ts} + {/if} + {if $permissions.edit && $ooff.edit_link} {ts domain="org.project60.sepa"}Edit{/ts} {/if} @@ -116,6 +128,9 @@ {else}
{ts domain="org.project60.sepa"}This contact has no recorded one-off mandates.{/ts} +{if $financialacls} + {ts domain="org.project60.sepa"}Note that only mandates associated with contributions of authorized financial types are being displayed.{/ts} +{/if}
{/if} diff --git a/xml/Menu/sepa.xml b/xml/Menu/sepa.xml index b2e561f3..4b835d0e 100644 --- a/xml/Menu/sepa.xml +++ b/xml/Menu/sepa.xml @@ -79,7 +79,7 @@ civicrm/sepa/tab CRM_Sepa_Page_MandateTab SEPA Mandates - access CiviCRM + view sepa mandates civicrm/sepa/retry diff --git a/xml/schema/CRM/Sepa/TransactionGroup.xml b/xml/schema/CRM/Sepa/TransactionGroup.xml index 4d8c9953..1e22be87 100644 --- a/xml/schema/CRM/Sepa/TransactionGroup.xml +++ b/xml/schema/CRM/Sepa/TransactionGroup.xml @@ -1,21 +1,21 @@ - + - CRM/Sepa - SEPATransactionGroup - civicrm_sdd_txgroup + CRM/Sepa + SEPATransactionGroup + civicrm_sdd_txgrouptrue - - id - int unsigned - true - ID + + id + int unsigned + true + ID true - - - id - true - + + + id + true + reference @@ -41,6 +41,13 @@ datetime Target collection date + + + financial_type_id + int unsigned + Financial type of contained contributions if CiviSEPA is generating groups matching financial types. + + latest_submission_date datetime @@ -67,10 +74,10 @@ sdd_creditor_id 4.3 - - sdd_creditor_id -
civicrm_sdd_creditor
- id + + sdd_creditor_id + civicrm_sdd_creditor
+ id SET NULL
@@ -83,10 +90,10 @@ file_id sdd_file_id - - sdd_file_id - civicrm_sdd_file
- id + + sdd_file_id + civicrm_sdd_file
+ id SET NULL