diff --git a/CRM/Twingle/Form/Profile.php b/CRM/Twingle/Form/Profile.php index 975a198..718a0e9 100644 --- a/CRM/Twingle/Form/Profile.php +++ b/CRM/Twingle/Form/Profile.php @@ -129,6 +129,28 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form { TRUE // is required ); + $this->add( + 'select', + 'gender_male', + E::ts('Gender option for submitted value "male"'), + $this->getGenderOptions(), + TRUE + ); + $this->add( + 'select', + 'gender_female', + E::ts('Gender option for submitted value "female"'), + $this->getGenderOptions(), + TRUE + ); + $this->add( + 'select', + 'gender_other', + E::ts('Gender option for submitted value "other"'), + $this->getGenderOptions(), + TRUE + ); + $payment_instruments = CRM_Twingle_Profile::paymentInstruments(); $this->assign('payment_instruments', $payment_instruments); foreach ($payment_instruments as $pi_name => $pi_label) { @@ -141,6 +163,16 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form { ); } + if (CRM_Twingle_Submission::civiSepaEnabled()) { + $this->add( + 'select', + 'sepa_creditor_id', + E::ts('CiviSEPA creditor'), + $this->getSepaCreditors(), + TRUE + ); + } + $this->add( 'select', // field type 'newsletter_groups', // field name @@ -282,17 +314,42 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form { * Retrieves campaigns present within the system as options for select form * elements. */ - public function getCampaigns() { - $campaigns = array('' => E::ts("no campaign")); - $query = civicrm_api3('Campaign', 'get', array( + public function getGenderOptions() { + $genders = array(); + $query = civicrm_api3('OptionValue', 'get', array( + 'option_group_id' => 'gender', 'is_active' => 1, 'option.limit' => 0, - 'return' => 'id,title' + 'return' => array( + 'value', + 'label', + ), )); - foreach ($query['values'] as $campaign) { - $campaigns[$campaign['id']] = $campaign['title']; + foreach ($query['values'] as $gender) { + $genders[$gender['value']] = $gender['label']; } - return $campaigns; + return $genders; + } + + /** + * Retrieves CiviSEPA creditors as options for select form elements. + * + * @return array + * @throws \CiviCRM_API3_Exception + */ + public function getSepaCreditors() { + $creditors = array(); + + if (CRM_Twingle_Submission::civiSepaEnabled()) { + $result = civicrm_api3('SepaCreditor', 'get', array( + 'option.limit' => 0, + )); + foreach ($result['values'] as $sepa_creditor) { + $creditors[$sepa_creditor['id']] = $sepa_creditor['name']; + } + } + + return $creditors; } /** @@ -309,7 +366,21 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form { 'return' => 'value,label' )); foreach ($query['values'] as $payment_instrument) { - self::$_paymentInstruments[$payment_instrument['value']] = $payment_instrument['label']; + // Do not include CiviSEPA payment instruments, but add a SEPA option if + // enabled. + if ( + CRM_Twingle_Submission::civiSepaEnabled() + && CRM_Sepa_Logic_Settings::isSDD(array( + 'payment_instrument_id' => $payment_instrument['value'], + )) + ) { + if (!isset(self::$_paymentInstruments['sepa'])) { + self::$_paymentInstruments['sepa'] = E::ts('CiviSEPA'); + } + } + else { + self::$_paymentInstruments[$payment_instrument['value']] = $payment_instrument['label']; + } } } return self::$_paymentInstruments; @@ -338,7 +409,23 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form { } } else { - $groups[''] = E::ts('No newsletter groups available'); + $groups[''] = E::ts('No mailing lists available'); + } + return $groups; + } + + /** + * Retrieves active groups as options for select form elements. + */ + public function getGroups() { + $groups = array(); + $query = civicrm_api3('Group', 'get', array( + 'is_active' => 1, + 'option.limit' => 0, + 'return' => 'id,name' + )); + foreach ($query['values'] as $group) { + $groups[$group['id']] = $group['name']; } return $groups; } @@ -348,27 +435,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form { * options for select form elements. */ public function getPostinfoGroups() { - $groups = array(); - $group_types = civicrm_api3('OptionValue', 'get', array( - 'option_group_id' => 'group_type', - 'name' => CRM_Twingle_Submission::GROUP_TYPE_POSTINFO, - )); - if ($group_types['count'] > 0) { - $group_type = reset($group_types['values']); - $query = civicrm_api3('Group', 'get', array( - 'is_active' => 1, - 'group_type' => array('LIKE' => '%' . CRM_Utils_Array::implodePadded($group_type['value']) . '%'), - 'option.limit' => 0, - 'return' => 'id,name' - )); - foreach ($query['values'] as $group) { - $groups[$group['id']] = $group['name']; - } - } - else { - $groups[''] = E::ts('No postal mailing groups available'); - } - return $groups; + return $this->getGroups(); } /** @@ -376,27 +443,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form { * system as options for select form elements. */ public function getDonationReceiptGroups() { - $groups = array(); - $group_types = civicrm_api3('OptionValue', 'get', array( - 'option_group_id' => 'group_type', - 'name' => CRM_Twingle_Submission::GROUP_TYPE_DONATION_RECEIPT, - )); - if ($group_types['count'] > 0) { - $group_type = reset($group_types['values']); - $query = civicrm_api3('Group', 'get', array( - 'is_active' => 1, - 'group_type' => array('LIKE' => '%' . CRM_Utils_Array::implodePadded($group_type['value']) . '%'), - 'option.limit' => 0, - 'return' => 'id,name' - )); - foreach ($query['values'] as $group) { - $groups[$group['id']] = $group['name']; - } - } - else { - $groups[''] = E::ts('No donation receipt groups available'); - } - return $groups; + return $this->getGroups(); } } diff --git a/CRM/Twingle/Profile.php b/CRM/Twingle/Profile.php index ff031e2..aa6bd0f 100644 --- a/CRM/Twingle/Profile.php +++ b/CRM/Twingle/Profile.php @@ -180,6 +180,10 @@ class CRM_Twingle_Profile { 'pi_paydirekt', 'pi_applepay', 'pi_googlepay', + 'sepa_creditor_id', + 'gender_male', + 'gender_female', + 'gender_other', 'newsletter_groups', 'postinfo_groups', 'donation_receipt_groups' @@ -221,19 +225,23 @@ class CRM_Twingle_Profile { 'location_type_id' => CRM_Twingle_Submission::LOCATION_TYPE_ID_WORK, 'financial_type_id' => 1, // "Donation" 'pi_banktransfer' => 5, // "EFT" - 'pi_debit_manual' => '', // TODO: SEPA + 'pi_debit_manual' => NULL, 'pi_debit_automatic' => 3, // Debit 'pi_creditcard' => 1, // "Credit Card" - 'pi_mobilephone_germany' => '', - 'pi_paypal' => '', - 'pi_sofortueberweisung' => '', - 'pi_amazonpay' => '', - 'pi_paydirekt' => '', - 'pi_applepay' => '', - 'pi_googlepay' => '', - 'newsletter_groups' => '', - 'postinfo_groups' => '', - 'donation_receipt_groups' => '', + 'pi_mobilephone_germany' => NULL, + 'pi_paypal' => NULL, + 'pi_sofortueberweisung' => NULL, + 'pi_amazonpay' => NULL, + 'pi_paydirekt' => NULL, + 'pi_applepay' => NULL, + 'pi_googlepay' => NULL, + 'sepa_creditor_id' => NULL, + 'gender_male' => 2, + 'gender_female' => 1, + 'gender_other' => 3, + 'newsletter_groups' => NULL, + 'postinfo_groups' => NULL, + 'donation_receipt_groups' => NULL, )); } diff --git a/CRM/Twingle/Submission.php b/CRM/Twingle/Submission.php index 06db85f..5356139 100644 --- a/CRM/Twingle/Submission.php +++ b/CRM/Twingle/Submission.php @@ -27,16 +27,6 @@ class CRM_Twingle_Submission { */ const GROUP_TYPE_NEWSLETTER = 'Mailing List'; - /** - * The option value name of the group type for postal mailing subscribers. - */ - const GROUP_TYPE_POSTINFO = ''; // TODO. - - /** - * The option value name of the group type for donation receipt requesters. - */ - const GROUP_TYPE_DONATION_RECEIPT = ''; // TODO. - /** * The default ID of the "Employer of" relationship type. */ @@ -100,7 +90,7 @@ class CRM_Twingle_Submission { // Get the gender ID defined within the profile, or return an error if none // matches (i.e. an unknown gender was submitted). - if (!$gender_id = $profile->getAttribute('gender_' . $params['user_gender'])) { + if (!empty($params['user_gender']) && !$gender_id = $profile->getAttribute('gender_' . $params['user_gender'])) { throw new CiviCRM_API3_Exception( E::ts('Gender could not be matched to existing gender.'), 'invalid_format' @@ -249,4 +239,60 @@ class CRM_Twingle_Submission { } } + /** + * Check whether the CiviSEPA extension is installed and CiviSEPA + * functionality is activated within the Twingle extension settings. + * + * @return bool + * @throws \CiviCRM_API3_Exception + */ + public static function civiSepaEnabled() { + $sepa_extension = civicrm_api3('Extension', 'get', array( + 'full_name' => 'org.project60.sepa', + 'is_active' => 1, + )); + return + CRM_Core_BAO_Setting::getItem( + 'de.systopia.twingle', + 'twingle_use_sepa' + ) + && $sepa_extension['count']; + } + + /** + * Retrieves recurring contribution frequency attributes for a given donation + * rhythm parameter value, according to a static mapping. + * + * @param string $donation_rhythm + * The submitted "donation_rhythm" paramter according to the API action + * specification. + * + * @return array + * An array with "frequency_unit" and "frequency_interval" keys, to be added + * to contribution parameter arrays. + */ + public static function getFrequencyMapping($donation_rhythm) { + $mapping = array( + 'halfyearly' => array( + 'frequency_unit' => 'month', + 'frequency_interval' => 6, + ), + 'quarterly' => array( + 'frequency_unit' => 'month', + 'frequency_interval' => 3, + ), + 'yearly' => array( + 'frequency_unit' => 'year', + 'frequency_interval' => 1, + ), + 'monthly' => array( + 'frequency_unit' => 'month', + 'frequency_interval' => 1, + ), + 'one_time' => array(), + ); + + return $mapping[$donation_rhythm]; + } + } diff --git a/api/v3/TwingleDonation/Submit.php b/api/v3/TwingleDonation/Submit.php index c26faf1..cfb8ba0 100644 --- a/api/v3/TwingleDonation/Submit.php +++ b/api/v3/TwingleDonation/Submit.php @@ -251,7 +251,10 @@ function civicrm_api3_twingle_donation_Submit($params) { $existing_contribution = civicrm_api3('Contribution', 'get', array( 'trxn_id' => $params['trx_id'] )); - if ($existing_contribution['count'] > 0) { + $existing_contribution_recur = civicrm_api3('ContributionRecur', 'get', array( + 'trxn_id' => $params['trx_id'] + )); + if ($existing_contribution['count'] > 0 || $existing_contribution_recur['count'] > 0) { throw new CiviCRM_API3_Exception( E::ts('Contribution with the given transaction ID already exists.'), 'api_error' @@ -298,19 +301,17 @@ function civicrm_api3_twingle_donation_Submit($params) { // Exclude address for now when retrieving/creating the individual contact // and an organisation is given, as we are checking organisation address // first and share it with the individual. - if (!empty($params['organization_name'])) { - $submitted_address = array(); - foreach (array( - 'street_address', - 'postal_code', - 'city', - 'country', - 'location_type_id', - ) as $address_component) { - if (!empty($params[$address_component])) { - $submitted_address[$address_component] = $params[$address_component]; - unset($params[$address_component]); - } + $submitted_address = array(); + foreach (array( + 'street_address', + 'postal_code', + 'city', + 'country', + 'location_type_id', + ) as $address_component) { + if (!empty($params[$address_component])) { + $submitted_address[$address_component] = $params[$address_component]; + unset($params[$address_component]); } } @@ -324,6 +325,7 @@ function civicrm_api3_twingle_donation_Submit($params) { 'user_birthdate' => 'birth_date', 'user_email' => 'email', 'user_telephone' => 'phone', + 'user_title' => 'formal_title', ) as $contact_param => $contact_component) { if (!empty($params[$contact_param])) { $contact_data[$contact_component] = $params[$contact_param]; @@ -354,7 +356,9 @@ function civicrm_api3_twingle_donation_Submit($params) { 'organization_name' => $params['organization_name'], ); if (!empty($submitted_address)) { - $params += $submitted_address; + $organisation_data += $submitted_address; + // Always use WORK address for organisation address. + $organisation_data['location_type_id'] = CRM_Twingle_Submission::LOCATION_TYPE_ID_WORK; } if (!$organisation_id = CRM_Twingle_Submission::getContact( 'Organization', @@ -366,16 +370,18 @@ function civicrm_api3_twingle_donation_Submit($params) { ); } } + // Share organisation address as WORK address with individual contact. $address_shared = ( isset($organisation_id) && CRM_Twingle_Submission::shareWorkAddress( $contact_id, $organisation_id, - $params['location_type_id'] + CRM_Twingle_Submission::LOCATION_TYPE_ID_WORK ) ); - // Address is not shared, use submitted address. + // Address is not shared, use submitted address with configured location + // type. if (!$address_shared && !empty($submitted_address)) { $submitted_address['contact_id'] = $contact_id; civicrm_api3('Address', 'create', $submitted_address); @@ -387,45 +393,88 @@ function civicrm_api3_twingle_donation_Submit($params) { } } - // TODO: contact into newsletter, postinfo and donation_receipt groups. + // If requested, add contact to newsletter groups defined in the profile. + if (!empty($params['newsletter']) && !empty($groups = $profile->getAttribute('newsletter_groups'))) { + foreach ($groups as $group_id) { + civicrm_api3('GroupContact', 'create', array( + 'group_id' => $group_id, + 'contact_id' => $contact_id, + )); + } + } - // Create contribution or SEPA mandate. + // If requested, add contact to postinfo groups defined in the profile. + if (!empty($params['postinfo']) && !empty($groups = $profile->getAttribute('postinfo_groups'))) { + foreach ($groups as $group_id) { + civicrm_api3('GroupContact', 'create', array( + 'group_id' => $group_id, + 'contact_id' => $contact_id, + )); + } + } + + // If requested, add contact to donation_receipt groups defined in the + // profile. + if (!empty($params['donation_receipt']) && !empty($groups = $profile->getAttribute('donation_receipt_groups'))) { + foreach ($groups as $group_id) { + civicrm_api3('GroupContact', 'create', array( + 'group_id' => $group_id, + 'contact_id' => $contact_id, + )); + } + } + + // Create contribution or SEPA mandate. Those attributes are valid for both, + // single and recurring contributions. $contribution_data = array( 'contact_id' => (isset($organisation_id) ? $organisation_id : $contact_id), 'currency' => $params['currency'], 'trxn_id' => $params['trx_id'], 'financial_type_id' => $profile->getAttribute('financial_type_id'), 'payment_instrument_id' => $params['payment_instrument_id'], - 'amount' => $params['amount'], - 'total_amount' => $params['amount'], + 'amount' => $params['amount'] / 100, + 'total_amount' => $params['amount'] / 100, ); if (!empty($params['purpose'])) { $contribution_data['note'] = $params['purpose']; } - $sepa_extension = civicrm_api3('Extension', 'get', array( - 'full_name' => 'org.project60.sepa', - 'is_active' => 1, - )); if ( - CRM_Core_BAO_Setting::getItem( - 'de.systopia.twingle', - 'twingle_use_sepa' - ) - && $sepa_extension['count'] - && CRM_Sepa_Logic_Settings::isSDD($contribution_data) + CRM_Twingle_Submission::civiSepaEnabled() + && $contribution_data['payment_instrument_id'] == 'sepa' ) { // If CiviSEPA is installed and the financial type is a CiviSEPA-one, // create SEPA mandate (and recurring contribution, using "createfull" API // action). - $mandate_data = $contribution_data + array( + foreach (array( + 'debit_iban', + 'debit_bic', + ) as $sepa_attribute) { + if (empty($params[$sepa_attribute])) { + throw new CiviCRM_API3_Exception( + E::ts('Missing attribute %1 for SEPA mandate', array( + 1 => $sepa_attribute, + )), + 'invalid_format' + ); + } + } + + $mandate_data = + $contribution_data + // Add CiviSEPA mandate attributes. + + array( 'type' => ($params['donation_rhythm'] == 'one_time' ? 'OOFF' : 'RCUR'), 'iban' => $params['debit_iban'], 'bic' => $params['debit_bic'], 'reference' => $params['debit_mandate_reference'], 'date' => $params['confirmed_at'], 'creditor_id' => $profile->getAttribute('sepa_creditor_id'), - ); + ) + // Add frequency unit and interval from static mapping. + + CRM_Twingle_Submission::getFrequencyMapping($params['donation_rhythm']); + // Let CiviSEPA set the correct payment instrument. + unset($mandate_data['payment_instrument_id']); $mandate = civicrm_api3('SepaMandate', 'createfull', $mandate_data); if ($mandate['is_error']) { throw new CiviCRM_API3_Exception( @@ -434,17 +483,19 @@ function civicrm_api3_twingle_donation_Submit($params) { ); } - $result_values = $mandate; + $result_values = $mandate['values']; } else { // Create (recurring) contribution. if ($params['donation_rhythm'] != 'one_time') { // Create recurring contribution first. - $contribution_recur_data = $contribution_data + array( - 'frequency_interval' => '', // TODO + $contribution_recur_data = + $contribution_data + + array( 'contribution_status_id' => 'Pending', // TODO: Or "In Progress"? 'start_date' => $params['confirmed_at'], - ); + ) + + CRM_Twingle_Submission::getFrequencyMapping($params['donation_rhythm']); $contribution_recur = civicrm_api3('contributionRecur', 'create', $contribution_recur_data); if ($contribution_recur['is_error']) { throw new CiviCRM_API3_Exception( @@ -468,7 +519,7 @@ function civicrm_api3_twingle_donation_Submit($params) { ); } - $result_values = $contribution; + $result_values = $contribution['values']; } $result = civicrm_api3_create_success($result_values); diff --git a/templates/CRM/Twingle/Form/Profile.tpl b/templates/CRM/Twingle/Form/Profile.tpl index 6931fe5..62b2d8a 100644 --- a/templates/CRM/Twingle/Form/Profile.tpl +++ b/templates/CRM/Twingle/Form/Profile.tpl @@ -42,6 +42,26 @@