Merge branch 'dev_20'
[#20] Alternative to blocking cancelling/ending recurring contributions
This commit is contained in:
commit
5f4bd857d8
10 changed files with 441 additions and 226 deletions
37
CRM/Twingle/Config.php
Normal file
37
CRM/Twingle/Config.php
Normal file
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
/*------------------------------------------------------------+
|
||||
| SYSTOPIA Twingle Integration |
|
||||
| Copyright (C) 2020 SYSTOPIA |
|
||||
| Author: B. Endres (endres@systopia.de) |
|
||||
+-------------------------------------------------------------+
|
||||
| This program is released as free software under the |
|
||||
| Affero GPL license. You can redistribute it and/or |
|
||||
| modify it under the terms of this license which you |
|
||||
| can read by viewing the included agpl.txt or online |
|
||||
| at www.gnu.org/licenses/agpl.html. Removal of this |
|
||||
| copyright header is strictly prohibited without |
|
||||
| written permission from the original author(s). |
|
||||
+-------------------------------------------------------------*/
|
||||
|
||||
use CRM_Twingle_ExtensionUtil as E;
|
||||
|
||||
class CRM_Twingle_Config {
|
||||
|
||||
const RCUR_PROTECTION_OFF = 0;
|
||||
const RCUR_PROTECTION_EXCEPTION = 1;
|
||||
const RCUR_PROTECTION_ACTIVITY = 2;
|
||||
|
||||
/**
|
||||
* Get the options for protecting a recurring contribution linked Twingle
|
||||
* against ending or cancellation (because Twingle would keep on collecting them)
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getRecurringProtectionOptions() {
|
||||
return [
|
||||
self::RCUR_PROTECTION_OFF => E::ts("No"),
|
||||
self::RCUR_PROTECTION_EXCEPTION => E::ts("Raise Exception"),
|
||||
self::RCUR_PROTECTION_ACTIVITY => E::ts("Create Activity"),
|
||||
];
|
||||
}
|
||||
}
|
|
@ -379,6 +379,13 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
|
|||
FALSE, // is not required
|
||||
array('class' => 'crm-select2 huge')
|
||||
);
|
||||
$this->add(
|
||||
'text',
|
||||
'membership_postprocess_call',
|
||||
E::ts('API Call for Membership Postprocessing'),
|
||||
FALSE
|
||||
);
|
||||
$this->addRule('membership_postprocess_call', E::ts("The API call must have the form 'Entity.Action'."), 'regex', '/^[A-Za-z_]+[.][A-Za-z_]+$/');
|
||||
|
||||
$this->add(
|
||||
'text', // field type
|
||||
|
|
|
@ -22,12 +22,18 @@ use CRM_Twingle_ExtensionUtil as E;
|
|||
*/
|
||||
class CRM_Twingle_Form_Settings extends CRM_Core_Form {
|
||||
|
||||
private $_settingFilter = array('group' => 'de.systopia.twingle');
|
||||
|
||||
//everything from this line down is generic & can be re-used for a setting form in another extension
|
||||
//actually - I lied - I added a specific call in getFormSettings
|
||||
private $_submittedValues = array();
|
||||
private $_settings = array();
|
||||
/**
|
||||
* @var array list of all settings options
|
||||
*/
|
||||
public static $SETTINGS_LIST = [
|
||||
'twingle_prefix',
|
||||
'twingle_use_sepa',
|
||||
'twingle_protect_recurring',
|
||||
'twingle_protect_recurring_activity_type',
|
||||
'twingle_protect_recurring_activity_subject',
|
||||
'twingle_protect_recurring_activity_status',
|
||||
'twingle_protect_recurring_activity_assignee',
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
|
@ -36,157 +42,132 @@ class CRM_Twingle_Form_Settings extends CRM_Core_Form {
|
|||
// Set redirect destination.
|
||||
$this->controller->_destination = CRM_Utils_System::url('civicrm/admin/settings/twingle', 'reset=1');
|
||||
|
||||
$settings = $this->getFormSettings();
|
||||
$form_elements = array();
|
||||
$this->add(
|
||||
'text',
|
||||
'twingle_prefix',
|
||||
E::ts("Twingle ID Prefix")
|
||||
);
|
||||
|
||||
foreach ($settings as $name => $setting) {
|
||||
if (isset($setting['quick_form_type'])) {
|
||||
$add = 'add' . $setting['quick_form_type'];
|
||||
if ($add == 'addElement') {
|
||||
$this->$add(
|
||||
$setting['html_type'],
|
||||
$name,
|
||||
ts($setting['title']),
|
||||
CRM_Utils_Array::value('html_attributes', $setting, array())
|
||||
);
|
||||
}
|
||||
elseif ($setting['html_type'] == 'Select') {
|
||||
$optionValues = array();
|
||||
if (!empty($setting['pseudoconstant']) && !empty($setting['pseudoconstant']['optionGroupName'])) {
|
||||
$optionValues = CRM_Core_OptionGroup::values($setting['pseudoconstant']['optionGroupName'], FALSE, FALSE, FALSE, NULL, 'name');
|
||||
}
|
||||
$this->add(
|
||||
'select',
|
||||
$setting['name'],
|
||||
$setting['title'],
|
||||
$optionValues,
|
||||
FALSE,
|
||||
CRM_Utils_Array::value('html_attributes', $setting, array())
|
||||
);
|
||||
}
|
||||
else {
|
||||
$this->$add($name, ts($setting['title']));
|
||||
}
|
||||
$form_elements[$setting['name']] = array('description' => ts($setting['description']));
|
||||
$this->add(
|
||||
'checkbox',
|
||||
'twingle_use_sepa',
|
||||
E::ts("Use CiviSEPA")
|
||||
);
|
||||
|
||||
// Disable CiviSEPA setting if the extension is not installed.
|
||||
if ($name == 'twingle_use_sepa') {
|
||||
$sepa_extension = civicrm_api3('Extension', 'get', array(
|
||||
'full_name' => 'org.project60.sepa',
|
||||
'is_active' => 1,
|
||||
));
|
||||
if ($sepa_extension['count'] == 0) {
|
||||
$element = $this->getElement('twingle_use_sepa');
|
||||
$element->freeze();
|
||||
$form_elements['twingle_use_sepa']['description'] .= ' <span class="error">The <a href="https://github.com/project60/org.project60.sepa" target="_blank" title="Extension page">CiviSEPA (<kbd>org.project60.sepa</kbd>) extension</a> is not installed.</span>';
|
||||
}
|
||||
$this->add(
|
||||
'select',
|
||||
'twingle_protect_recurring',
|
||||
E::ts("Protect Recurring Contributions"),
|
||||
CRM_Twingle_Config::getRecurringProtectionOptions()
|
||||
);
|
||||
|
||||
$this->add(
|
||||
'select',
|
||||
'twingle_protect_recurring_activity_type',
|
||||
E::ts("Activity Type"),
|
||||
$this->getOptionValueList('activity_type', [0])
|
||||
);
|
||||
|
||||
$this->add(
|
||||
'text',
|
||||
'twingle_protect_recurring_activity_subject',
|
||||
E::ts("Subject"),
|
||||
['class' => 'huge']
|
||||
);
|
||||
|
||||
$this->add(
|
||||
'select',
|
||||
'twingle_protect_recurring_activity_status',
|
||||
E::ts("Status"),
|
||||
$this->getOptionValueList('activity_status')
|
||||
);
|
||||
|
||||
$this->addEntityRef(
|
||||
'twingle_protect_recurring_activity_assignee',
|
||||
E::ts('Assigned To'),
|
||||
[
|
||||
'contact_type' => ['IN' => ['Individual', 'Organization']],
|
||||
'check_permissions' => 0,
|
||||
]
|
||||
);
|
||||
|
||||
$this->addButtons(array(
|
||||
array (
|
||||
'type' => 'submit',
|
||||
'name' => E::ts('Save'),
|
||||
'isDefault' => TRUE,
|
||||
)
|
||||
));
|
||||
|
||||
// set defaults
|
||||
foreach (self::$SETTINGS_LIST as $setting) {
|
||||
$this->setDefaults([
|
||||
$setting => Civi::settings()->get($setting)
|
||||
]);
|
||||
}
|
||||
|
||||
parent::buildQuickForm();
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom form validation, because the activity creation fields
|
||||
* are only mandatory if activity creation is active
|
||||
* @return bool
|
||||
*/
|
||||
public function validate() {
|
||||
parent::validate();
|
||||
|
||||
// if activity creation is active, make sure the fields are set
|
||||
$protection_mode = CRM_Utils_Array::value('twingle_protect_recurring', $this->_submitValues);
|
||||
if ($protection_mode == CRM_Twingle_Config::RCUR_PROTECTION_ACTIVITY) {
|
||||
foreach (['twingle_protect_recurring_activity_type',
|
||||
'twingle_protect_recurring_activity_subject',
|
||||
'twingle_protect_recurring_activity_status',
|
||||
'twingle_protect_recurring_activity_assignee',] as $activity_field) {
|
||||
$current_value = CRM_Utils_Array::value($activity_field, $this->_submitValues);
|
||||
if (empty($current_value)) {
|
||||
$this->_errors[$activity_field] = E::ts("This is required for activity creation");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->assign('formElements', $form_elements);
|
||||
|
||||
$this->addButtons(array(
|
||||
array (
|
||||
'type' => 'submit',
|
||||
'name' => ts('Save'),
|
||||
'isDefault' => TRUE,
|
||||
)
|
||||
));
|
||||
|
||||
// Export form elements.
|
||||
$this->assign('elementNames', $this->getRenderableElementNames());
|
||||
parent::buildQuickForm();
|
||||
return (0 == count($this->_errors));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
function postProcess() {
|
||||
$this->_submittedValues = $this->exportValues();
|
||||
$this->saveSettings();
|
||||
$values = $this->exportValues();
|
||||
|
||||
// store settings
|
||||
foreach (self::$SETTINGS_LIST as $setting) {
|
||||
Civi::settings()->set($setting, CRM_Utils_Array::value($setting, $values));
|
||||
}
|
||||
|
||||
parent::postProcess();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fields/elements defined in this form.
|
||||
*
|
||||
* @return array (string)
|
||||
*/
|
||||
function getRenderableElementNames() {
|
||||
// The _elements list includes some items which should not be
|
||||
// auto-rendered in the loop -- such as "qfKey" and "buttons". These
|
||||
// items don't have labels. We'll identify renderable by filtering on
|
||||
// the 'label'.
|
||||
$elementNames = array();
|
||||
foreach ($this->_elements as $element) {
|
||||
/* @var \HTML_QuickForm_element $element */
|
||||
$label = $element->getLabel();
|
||||
if (!empty($label)) {
|
||||
$elementNames[] = $element->getName();
|
||||
}
|
||||
}
|
||||
return $elementNames;
|
||||
}
|
||||
/**
|
||||
* Get the settings we are going to allow to be set on this form.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @throws \CiviCRM_API3_Exception
|
||||
*/
|
||||
function getFormSettings() {
|
||||
if (empty($this->_settings)) {
|
||||
$settings = civicrm_api3('setting', 'getfields', array('filters' => $this->_settingFilter));
|
||||
$settings = $settings['values'];
|
||||
}
|
||||
else {
|
||||
$settings = $this->_settings;
|
||||
}
|
||||
return $settings;
|
||||
}
|
||||
/**
|
||||
* Save the settings set on this form.
|
||||
*/
|
||||
function saveSettings() {
|
||||
$settings = $this->getFormSettings();
|
||||
$values = array_intersect_key($this->_submittedValues, $settings);
|
||||
civicrm_api3('setting', 'create', $values);
|
||||
}
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
function setDefaultValues() {
|
||||
$existing = civicrm_api3('setting', 'get', array('return' => array_keys($this->getFormSettings())));
|
||||
$defaults = array();
|
||||
$domainID = CRM_Core_Config::domainID();
|
||||
foreach ($existing['values'][$domainID] as $name => $value) {
|
||||
$defaults[$name] = $value;
|
||||
}
|
||||
return $defaults;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* Get a list of option group items
|
||||
* @param $group_id string group ID or name
|
||||
* @return array list of ID(value) => label
|
||||
* @throws CiviCRM_API3_Exception
|
||||
*/
|
||||
public function addRules() {
|
||||
$this->addFormRule(array('CRM_Twingle_Form_Settings', 'validateSettingsForm'));
|
||||
protected function getOptionValueList($group_id, $reserved = [0,1]) {
|
||||
$list = ['' => E::ts("-select-")];
|
||||
$query = civicrm_api3('OptionValue', 'get', [
|
||||
'option_group_id' => $group_id,
|
||||
'option.limit' => 0,
|
||||
'is_active' => 1,
|
||||
'is_reserved' => ['IN' => $reserved],
|
||||
'return' => 'value,label',
|
||||
]);
|
||||
foreach ($query['values'] as $value) {
|
||||
$list[$value['value']] = $value['label'];
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the profile form.
|
||||
*
|
||||
* @param array $values
|
||||
* The submitted form values, keyed by form element name.
|
||||
*
|
||||
* @return bool | array
|
||||
* TRUE when the form was successfully validated, or an array of error
|
||||
* messages, keyed by form element name.
|
||||
*/
|
||||
public static function validateSettingsForm($values) {
|
||||
$errors = array();
|
||||
|
||||
return empty($errors) ? TRUE : $errors;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -219,6 +219,7 @@ class CRM_Twingle_Profile {
|
|||
'custom_field_mapping',
|
||||
'membership_type_id',
|
||||
'membership_type_id_recur',
|
||||
'membership_postprocess_call',
|
||||
),
|
||||
// Add payment methods.
|
||||
array_keys(static::paymentInstruments()),
|
||||
|
|
|
@ -41,29 +41,109 @@ class CRM_Twingle_Tools {
|
|||
// check if we're suspended
|
||||
if (self::$protection_suspended) return;
|
||||
|
||||
// currently only works with prefixes
|
||||
$prefix = Civi::settings()->get('twingle_prefix');
|
||||
if (empty($prefix)) return;
|
||||
|
||||
// check if protection is turned on
|
||||
$protection_on = Civi::settings()->get('twingle_protect_recurring');
|
||||
if (empty($protection_on)) return;
|
||||
|
||||
// load the recurring contribution
|
||||
$recurring_contribution = civicrm_api3('ContributionRecur', 'getsingle', [
|
||||
'return' => 'trxn_id,contribution_status_id,payment_instrument_id',
|
||||
'return' => 'trxn_id,contribution_status_id,payment_instrument_id,contact_id',
|
||||
'id' => $recurring_contribution_id]);
|
||||
|
||||
// check if this is a SEPA transaction
|
||||
// check if this is a SEPA transaction (doesn't concern us)
|
||||
if (self::isSDD($recurring_contribution['payment_instrument_id'])) return;
|
||||
|
||||
// see if this recurring contribution is from Twingle
|
||||
if (!self::isTwingleRecurringContribution($recurring_contribution_id, $recurring_contribution)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// check if it's really a termination (i.e. current status is 2 or 5)
|
||||
if (!in_array($recurring_contribution['contribution_status_id'], [2,5])) return;
|
||||
|
||||
// check if it's a Twingle contribution
|
||||
if (substr($recurring_contribution['trxn_id'], 0, strlen($prefix)) == $prefix) {
|
||||
// this is a Twingle contribution that is about to be terminated
|
||||
throw new Exception(E::ts("This is a Twingle recurring contribution. It should be terminated through the Twingle interface, otherwise it will still be collected."));
|
||||
// this _IS_ on of the cases where we should step in:
|
||||
CRM_Twingle_Tools::processRecurringContributionTermination($recurring_contribution_id, $recurring_contribution);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $recurring_contribution_id int recurring contribution ID to check
|
||||
* @param $recurring_contribution array recurring contribution data, optional
|
||||
* @return bool|null true, false or null if can't be determined
|
||||
* @throws CiviCRM_API3_Exception
|
||||
*/
|
||||
public static function isTwingleRecurringContribution($recurring_contribution_id, $recurring_contribution = NULL) {
|
||||
// this currently only works with prefixes
|
||||
$prefix = Civi::settings()->get('twingle_prefix');
|
||||
if (empty($prefix)) return null;
|
||||
|
||||
// load recurring contribution if necessary
|
||||
if (empty($recurring_contribution['trxn_id'])) {
|
||||
$recurring_contribution = civicrm_api3('ContributionRecur', 'getsingle', ['id' => $recurring_contribution_id]);
|
||||
}
|
||||
|
||||
// check if it's a Twingle contribution by checking the prefix
|
||||
// fixme: better ways (e.g. tags) should be used to mark twingle contributions
|
||||
return (substr($recurring_contribution['trxn_id'], 0, strlen($prefix)) == $prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the recurring contribution protection
|
||||
*
|
||||
* @param $recurring_contribution_id int recurring contribution ID
|
||||
* @param $recurring_contribution array recurring contribution fields
|
||||
* @throws Exception could be one of the measures
|
||||
*/
|
||||
public static function processRecurringContributionTermination($recurring_contribution_id, $recurring_contribution) {
|
||||
// check if we're suspended
|
||||
if (self::$protection_suspended) return;
|
||||
|
||||
$protection_mode = Civi::settings()->get('twingle_protect_recurring');
|
||||
switch ($protection_mode) {
|
||||
case CRM_Twingle_Config::RCUR_PROTECTION_OFF:
|
||||
// do nothing
|
||||
break;
|
||||
|
||||
case CRM_Twingle_Config::RCUR_PROTECTION_EXCEPTION:
|
||||
throw new Exception(E::ts("This is a Twingle recurring contribution. It should be terminated through the Twingle interface, otherwise it will still be collected."));
|
||||
|
||||
case CRM_Twingle_Config::RCUR_PROTECTION_ACTIVITY:
|
||||
// create contact source activity
|
||||
// first: get the contact ID
|
||||
if (!empty($recurring_contribution['contact_id'])) {
|
||||
$target_id = (int) $recurring_contribution['contact_id'];
|
||||
} else {
|
||||
$target_id = (int) civicrm_api3('ContributionRecur', 'getvalue', [
|
||||
'id' => $recurring_contribution_id,
|
||||
'return' => 'contact_id']);
|
||||
}
|
||||
if (!empty($recurring_contribution['trxn_id'])) {
|
||||
$trxn_id = $recurring_contribution['trxn_id'];
|
||||
} else {
|
||||
$trxn_id = civicrm_api3('ContributionRecur', 'getvalue', [
|
||||
'id' => $recurring_contribution_id,
|
||||
'return' => 'trxn_id']);
|
||||
}
|
||||
|
||||
try {
|
||||
civicrm_api3('Activity', 'create', [
|
||||
'activity_type_id' => Civi::settings()->get('twingle_protect_recurring_activity_type'),
|
||||
'subject' => Civi::settings()->get('twingle_protect_recurring_activity_subject'),
|
||||
'activity_date_time' => date('YmdHis'),
|
||||
'target_id' => $target_id,
|
||||
'assignee_id' => Civi::settings()->get('twingle_protect_recurring_activity_assignee'),
|
||||
'status_id' => Civi::settings()->get('twingle_protect_recurring_activity_status'),
|
||||
'details' => E::ts("Recurring contribution [%1] (Transaction ID '%2') was terminated by a user. You need to end the corresponding record in Twingle as well, or it will still be collected.",
|
||||
[1 => $recurring_contribution_id, 2 => $trxn_id]),
|
||||
'source_contact_id' => CRM_Core_Session::getLoggedInContactID(),
|
||||
]);
|
||||
} catch (Exception $ex) {
|
||||
Civi::log()->debug("TwingleAPI: Couldn't create recurring protection activity: " . $ex->getMessage());
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
Civi::log()->debug("TwingleAPI: Unknown recurring contribution protection mode: '{$protection_mode}'");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -693,11 +693,43 @@ function civicrm_api3_twingle_donation_Submit($params) {
|
|||
}
|
||||
if (!empty($membership_type_id)) {
|
||||
$membership = civicrm_api3('Membership', 'create', array(
|
||||
'contact_id' => $contact_id,
|
||||
'membership_type_id' => $membership_type_id,
|
||||
'contact_id' => $contact_id,
|
||||
'membership_type_id' => $membership_type_id,
|
||||
));
|
||||
|
||||
$result_values['membership'] = $membership;
|
||||
|
||||
// call the postprocess API
|
||||
$postprocess_call = $profile->getAttribute('membership_postprocess_call');
|
||||
if (!empty($postprocess_call)) {
|
||||
list($pp_entity, $pp_action) = explode('.', $postprocess_call, 2);
|
||||
try {
|
||||
// gather the contribution IDs
|
||||
$recurring_contribution_id = $contribution_id = '';
|
||||
if (isset($contribution_recur['id'])) {
|
||||
$recurring_contribution_id = $contribution_recur['id'];
|
||||
}
|
||||
if (isset($contribution['id'])) {
|
||||
$contribution_id = $contribution['id'];
|
||||
}
|
||||
|
||||
// run the call
|
||||
civicrm_api3($pp_entity, $pp_action, [
|
||||
'membership_id' => $membership['id'],
|
||||
'contact_id' => $contact_id,
|
||||
'organization_id' => isset($organisation_id) ? $organisation_id : '',
|
||||
'contribution_id' => $contribution_id,
|
||||
'recurring_contribution_id' => $recurring_contribution_id,
|
||||
]);
|
||||
|
||||
// refresh membership data
|
||||
$result_values['membership'] = civicrm_api3('Membership', 'getsingle', $membership['id']);
|
||||
|
||||
} catch (Exception $ex) {
|
||||
// TODO: more error handling?
|
||||
Civi::log()->debug("Twingle membership postprocessing call {$pp_entity}.{$pp_action} has failed: " . $ex->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$result = civicrm_api3_create_success($result_values);
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
<?php
|
||||
/*------------------------------------------------------------+
|
||||
| SYSTOPIA Twingle Integration |
|
||||
| Copyright (C) 2018 SYSTOPIA |
|
||||
| Author: J. Schuppe (schuppe@systopia.de) |
|
||||
+-------------------------------------------------------------+
|
||||
| This program is released as free software under the |
|
||||
| Affero GPL license. You can redistribute it and/or |
|
||||
| modify it under the terms of this license which you |
|
||||
| can read by viewing the included agpl.txt or online |
|
||||
| at www.gnu.org/licenses/agpl.html. Removal of this |
|
||||
| copyright header is strictly prohibited without |
|
||||
| written permission from the original author(s). |
|
||||
+-------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* Settings metadata file
|
||||
*/
|
||||
return array(
|
||||
'twingle_use_sepa' => array(
|
||||
'group_name' => 'de.systopia.twingle',
|
||||
'group' => 'de.systopia.twingle',
|
||||
'name' => 'twingle_use_sepa',
|
||||
'type' => 'Boolean',
|
||||
'quick_form_type' => 'YesNo',
|
||||
'html_type' => 'radio',
|
||||
'title' => 'Use CiviSEPA',
|
||||
'default' => 0,
|
||||
'add' => '4.6',
|
||||
'is_domain' => 1,
|
||||
'is_contact' => 0,
|
||||
'description' => 'Whether to provide CiviSEPA functionality for manual debit payment method. This requires the CiviSEPA (org.project60.sepa) extension be installed.',
|
||||
),
|
||||
'twingle_protect_recurring' => array(
|
||||
'group_name' => 'de.systopia.twingle',
|
||||
'group' => 'de.systopia.twingle',
|
||||
'name' => 'twingle_protect_recurring',
|
||||
'type' => 'Boolean',
|
||||
'quick_form_type' => 'YesNo',
|
||||
'html_type' => 'radio',
|
||||
'title' => 'Protect Recurring Contributions',
|
||||
'default' => 0,
|
||||
'add' => '4.6',
|
||||
'is_domain' => 1,
|
||||
'is_contact' => 0,
|
||||
'description' => 'Will protect all recurring contributions created by Twingle from termination, since this does NOT terminate the Twingle collection process. Currently only works with a prefix',
|
||||
),
|
||||
'twingle_prefix' => array(
|
||||
'group_name' => 'de.systopia.twingle',
|
||||
'group' => 'de.systopia.twingle',
|
||||
'name' => 'twingle_prefix',
|
||||
'type' => CRM_Utils_Type::T_STRING,
|
||||
'quick_form_type' => 'Element',
|
||||
'html_type' => 'text',
|
||||
'title' => 'Twingle ID Prefix',
|
||||
'default' => '',
|
||||
'add' => '4.6',
|
||||
'is_domain' => 1,
|
||||
'is_contact' => 0,
|
||||
'description' => 'You can use this setting to add a prefix to the Twingle transaction ID, in order to avoid collisions with other transaction ids.',
|
||||
),
|
||||
);
|
|
@ -38,6 +38,17 @@
|
|||
{ts domain="de.systopia.twingle"}Select which financial type to use for recurring contributions.{/ts}
|
||||
{/htxt}
|
||||
|
||||
{htxt id='id-membership-postprocessing-call'}
|
||||
{ts domain="de.systopia.twingle"}Some organisations have specific conventions on how a membership should be created. Since the Twingle-API can only create a "bare bone" membership object, you can enter a API Call (as 'Entity.Action') to adjust any newly created membership to your organisation's needs.{/ts}
|
||||
{ts domain="de.systopia.twingle"}The API call would receive the following parameters:<ul>
|
||||
<li><code>membership_id</code>: The ID of the newly created membership</li>
|
||||
<li><code>contact_id</code>: The ID of the contact involved</li>
|
||||
<li><code>organization_id</code>: The ID of the contact's organisation, potentially empty</li>
|
||||
<li><code>contribution_id</code>: The ID contribution received, potentially empty</li>
|
||||
<li><code>recurring_contribution_id</code>: The ID of the recurring contribution. If empty, this was only a one-off donation.</li>
|
||||
</ul>{/ts}
|
||||
{/htxt}
|
||||
|
||||
{htxt id='id-custom_field_mapping'}
|
||||
{ts domain="de.systopia.twingle"}<p>Map Twingle custom fields to CiviCRM custom fields using the following format (each assignment in a separate line):</p>
|
||||
<pre>twingle_field_1=custom_123<br />twingle_field_2=custom_789</pre>
|
||||
|
|
|
@ -236,6 +236,27 @@
|
|||
<td class="label">{$form.membership_type_id_recur.label}</td>
|
||||
<td class="content">{$form.membership_type_id_recur.html}</td>
|
||||
</tr>
|
||||
<tr class="crm-section twingle-postprocess-call">
|
||||
<td class="label">
|
||||
{$form.membership_postprocess_call.label}
|
||||
<a
|
||||
onclick='
|
||||
CRM.help(
|
||||
"{ts domain="de.systopia.twingle"}Membership Postprocessing{/ts}",
|
||||
{literal}{
|
||||
"id": "id-membership-postprocessing-call",
|
||||
"file": "CRM\/Twingle\/Form\/Profile"
|
||||
}{/literal}
|
||||
);
|
||||
return false;
|
||||
'
|
||||
href="#"
|
||||
title="{ts domain="de.systopia.twingle"}Help{/ts}"
|
||||
class="helpicon"
|
||||
></a>
|
||||
</td>
|
||||
<td class="content">{$form.membership_postprocess_call.html}</td>
|
||||
</tr>
|
||||
|
||||
<tr class="crm-section">
|
||||
<td class="label">{$form.contribution_source.label}</td>
|
||||
|
@ -286,3 +307,27 @@
|
|||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{literal}
|
||||
<script>
|
||||
/**
|
||||
* Update the form fields based on whether membership creation is currently active
|
||||
*/
|
||||
function twingle_membership_active_changed() {
|
||||
let active = cj('#membership_type_id').val() || cj('#membership_type_id_recur').val();
|
||||
if (active) {
|
||||
cj('#membership_postprocess_call').parent().parent().show();
|
||||
} else {
|
||||
cj('#membership_postprocess_call').val(''); // empty to avoid hidden validation fail
|
||||
cj('#membership_postprocess_call').parent().parent().hide();
|
||||
}
|
||||
}
|
||||
|
||||
// register events and run once
|
||||
cj(document).ready(function (){
|
||||
cj('#membership_type_id').change(twingle_membership_active_changed);
|
||||
cj('#membership_type_id_recur').change(twingle_membership_active_changed);
|
||||
});
|
||||
twingle_membership_active_changed();
|
||||
</script>
|
||||
{/literal}
|
|
@ -14,29 +14,112 @@
|
|||
|
||||
<div class="crm-block crm-form-block crm-twingle-form-block">
|
||||
|
||||
{* HEADER *}
|
||||
<div class="crm-submit-buttons">
|
||||
{include file="CRM/common/formButtons.tpl" location="top"}
|
||||
</div>
|
||||
<h3>Twingle API - Generic Settings</h3>
|
||||
|
||||
<table class="form-layout-compressed">
|
||||
{foreach from=$elementNames item=elementName}
|
||||
<tr class="crm-twingle-form-block-{$form.$elementName.name}">
|
||||
<td class="label">{$form.$elementName.label} <a onclick='CRM.help("{$form.$elementName.label}", {literal}{"id":"id-{/literal}{$form.$elementName.name}{literal}","file":"CRM\/Twingle\/Form\/Settings"}{/literal}); return false;' href="#" title="{ts domain="de.systopia.twingle"}Help{/ts}" class="helpicon"></a></td>
|
||||
<tr class="crm-twingle-form-block-use-sepa">
|
||||
<td class="label">{$form.twingle_use_sepa.label} <a onclick='CRM.help("{$form.twingle_use_sepa.label}", {literal}{"id":"id-{/literal}{$form.twingle_use_sepa.name}{literal}","file":"CRM\/Twingle\/Form\/Settings"}{/literal}); return false;' href="#" title="{ts domain="de.systopia.twingle"}Help{/ts}" class="helpicon"></a></td>
|
||||
<td>
|
||||
{$form.$elementName.html}
|
||||
{$form.twingle_use_sepa.html}
|
||||
<br />
|
||||
<span class="description">
|
||||
{$formElements.$elementName.description}
|
||||
{$formElements.twingle_use_sepa.description}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
|
||||
<tr class="crm-twingle-form-block-prefix">
|
||||
<td class="label">{$form.twingle_prefix.label} <a onclick='CRM.help("{$form.twingle_prefix.label}", {literal}{"id":"id-{/literal}{$form.twingle_prefix.name}{literal}","file":"CRM\/Twingle\/Form\/Settings"}{/literal}); return false;' href="#" title="{ts domain="de.systopia.twingle"}Help{/ts}" class="helpicon"></a></td>
|
||||
<td>
|
||||
{$form.twingle_prefix.html}
|
||||
<br />
|
||||
<span class="description">
|
||||
{$formElements.twingle_prefix.description}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr class="crm-twingle-form-block-recurring-protection">
|
||||
<td class="label">{$form.twingle_protect_recurring.label} <a onclick='CRM.help("{$form.twingle_protect_recurring.label}", {literal}{"id":"id-{/literal}{$form.twingle_protect_recurring.name}{literal}","file":"CRM\/Twingle\/Form\/Settings"}{/literal}); return false;' href="#" title="{ts domain="de.systopia.twingle"}Help{/ts}" class="helpicon"></a></td>
|
||||
<td>
|
||||
{$form.twingle_protect_recurring.html}
|
||||
<br />
|
||||
<span class="description">
|
||||
{$formElements.protect_recurring.description}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr class="crm-twingle-form-block-recurring-protection-activity">
|
||||
<td class="label">{$form.twingle_protect_recurring_activity_type.label}</td>
|
||||
<td>
|
||||
{$form.twingle_protect_recurring_activity_type.html}
|
||||
<br />
|
||||
<span class="description">
|
||||
{$formElements.twingle_protect_recurring_activity_type.description}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr class="crm-twingle-form-block-recurring-protection-activity">
|
||||
<td class="label">{$form.twingle_protect_recurring_activity_subject.label}</td>
|
||||
<td>
|
||||
{$form.twingle_protect_recurring_activity_subject.html}
|
||||
<br />
|
||||
<span class="description">
|
||||
{$formElements.twingle_protect_recurring_activity_subject.description}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr class="crm-twingle-form-block-recurring-protection-activity">
|
||||
<td class="label">{$form.twingle_protect_recurring_activity_status.label}</td>
|
||||
<td>
|
||||
{$form.twingle_protect_recurring_activity_status.html}
|
||||
<br />
|
||||
<span class="description">
|
||||
{$formElements.twingle_protect_recurring_activity_status.description}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr class="crm-twingle-form-block-recurring-protection-activity">
|
||||
<td class="label">{$form.twingle_protect_recurring_activity_assignee.label}</td>
|
||||
<td>
|
||||
{$form.twingle_protect_recurring_activity_assignee.html}
|
||||
<br />
|
||||
<span class="description">
|
||||
{$formElements.twingle_protect_recurring_activity_assignee.description}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
||||
{* FOOTER *}
|
||||
|
||||
<div class="crm-submit-buttons">
|
||||
{include file="CRM/common/formButtons.tpl" location="bottom"}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{literal}
|
||||
<script>
|
||||
/**
|
||||
* Will show/hide the twingle_protect_recurring_activity_* fields based on
|
||||
* whether activity creation is selected
|
||||
*/
|
||||
function twingle_protect_recurring_change() {
|
||||
if (cj("#twingle_protect_recurring").val() == '2') {
|
||||
cj("tr.crm-twingle-form-block-recurring-protection-activity").show();
|
||||
} else {
|
||||
cj("tr.crm-twingle-form-block-recurring-protection-activity").hide();
|
||||
}
|
||||
}
|
||||
|
||||
cj(document).ready(function () {
|
||||
cj("#twingle_protect_recurring").change(twingle_protect_recurring_change);
|
||||
twingle_protect_recurring_change();
|
||||
});
|
||||
</script>
|
||||
{/literal}
|
Loading…
Add table
Add a link
Reference in a new issue