CRM-17850 Allow payment processors to determine which edit fields are exposed
authoreileenmcnaughton <eileen@fuzion.co.nz>
Thu, 21 Jan 2016 01:02:18 +0000 (01:02 +0000)
committereileenmcnaughton <eileen@fuzion.co.nz>
Thu, 21 Jan 2016 01:56:23 +0000 (01:56 +0000)
CRM/Contribute/BAO/ContributionRecur.php
CRM/Contribute/Form/UpdateBilling.php
CRM/Contribute/Form/UpdateSubscription.php
CRM/Core/Payment.php
templates/CRM/Contribute/Form/UpdateSubscription.tpl
xml/schema/Contribute/ContributionRecur.xml

index 4ddab8cadf23bb6067da105686af63e7130136c6..b139c9469b3bde2c0229a2da9f0d6a2b6a13270c 100644 (file)
@@ -382,6 +382,9 @@ SELECT rec.id                   as recur_id,
        rec.currency,
        rec.campaign_id,
        rec.financial_type_id,
+       rec.next_sched_contribution_date,
+       rec.failure_retry_date,
+       rec.cycle_day,
        con.id as contribution_id,
        con.contribution_page_id,
        rec.contact_id,
index 588ac72a0e557204e85b28c60559908cb8cf08cb..25a1e4df2634c34c40c9b08cf24c4bec978f5f2f 100644 (file)
@@ -119,16 +119,10 @@ class CRM_Contribute_Form_UpdateBilling extends CRM_Core_Form {
   }
 
   /**
-   * This virtual function is used to set the default values of
-   * various form elements
+   * Set the default values of various form elements.
    *
-   * access        public
-   *
-   * @return array
-   *   reference to the array of default values
-   */
-  /**
    * @return array
+   *   Default values
    */
   public function setDefaultValues() {
     $this->_defaults = array();
@@ -211,7 +205,7 @@ class CRM_Contribute_Form_UpdateBilling extends CRM_Core_Form {
    *   The input form values.
    * @param array $files
    *   The uploaded files if any.
-   * @param $self
+   * @param CRM_Core_Form $self
    *
    *
    * @return bool|array
index fb61f50c46b0a0890eaaa524f010e0e75380c7b5..c081d1b640f12690ef80def629d39ab41a1174c5 100644 (file)
@@ -26,7 +26,6 @@
  */
 
 /**
- *
  * @package CRM
  * @copyright CiviCRM LLC (c) 2004-2015
  */
 class CRM_Contribute_Form_UpdateSubscription extends CRM_Core_Form {
 
   /**
-   * The recurring contribution id, used when editing the recurring contribution
+   * The recurring contribution id, used when editing the recurring contribution.
    *
    * @var int
    */
-  protected $_crid = NULL;
+  protected $contributionRecurID = NULL;
 
   protected $_coid = NULL;
 
@@ -57,6 +56,13 @@ class CRM_Contribute_Form_UpdateSubscription extends CRM_Core_Form {
 
   public $_paymentProcessorObj = NULL;
 
+  /**
+   * Fields that affect the schedule and are defined as editable by the processor.
+   *
+   * @var array
+   */
+  protected $editableScheduleFields = array();
+
   /**
    * The id of the contact associated with this recurring contribution.
    *
@@ -71,11 +77,14 @@ class CRM_Contribute_Form_UpdateSubscription extends CRM_Core_Form {
    */
   public function preProcess() {
 
-    $this->_crid = CRM_Utils_Request::retrieve('crid', 'Integer', $this, FALSE);
-    if ($this->_crid) {
-      $this->_paymentProcessor = CRM_Contribute_BAO_ContributionRecur::getPaymentProcessor($this->_crid);
+    $this->contributionRecurID = CRM_Utils_Request::retrieve('crid', 'Integer', $this, FALSE);
+    if ($this->contributionRecurID) {
+      $this->_paymentProcessor = CRM_Contribute_BAO_ContributionRecur::getPaymentProcessor($this->contributionRecurID);
+      if (!$this->_paymentProcessor) {
+        CRM_Core_Error::statusBounce(ts('There is no valid processor for this subscription so it cannot be edited.'));
+      }
       $this->_paymentProcessorObj = $this->_paymentProcessor['object'];
-      $this->_subscriptionDetails = CRM_Contribute_BAO_ContributionRecur::getSubscriptionDetails($this->_crid);
+      $this->_subscriptionDetails = CRM_Contribute_BAO_ContributionRecur::getSubscriptionDetails($this->contributionRecurID);
     }
 
     $this->_coid = CRM_Utils_Request::retrieve('coid', 'Integer', $this, FALSE);
@@ -83,13 +92,13 @@ class CRM_Contribute_Form_UpdateSubscription extends CRM_Core_Form {
       $this->_paymentProcessor = CRM_Financial_BAO_PaymentProcessor::getProcessorForEntity($this->_coid, 'contribute', 'info');
       $this->_paymentProcessorObj = CRM_Financial_BAO_PaymentProcessor::getProcessorForEntity($this->_coid, 'contribute', 'obj');
       $this->_subscriptionDetails = CRM_Contribute_BAO_ContributionRecur::getSubscriptionDetails($this->_coid, 'contribution');
-      $this->_crid = $this->_subscriptionDetails->recur_id;
+      $this->contributionRecurID = $this->_subscriptionDetails->recur_id;
     }
-    elseif ($this->_crid) {
-      $this->_coid = CRM_Core_DAO::getFieldValue('CRM_Contribute_DAO_Contribution', $this->_crid, 'id', 'contribution_recur_id');
+    elseif ($this->contributionRecurID) {
+      $this->_coid = CRM_Core_DAO::getFieldValue('CRM_Contribute_DAO_Contribution', $this->contributionRecurID, 'id', 'contribution_recur_id');
     }
 
-    if ((!$this->_crid) ||
+    if ((!$this->contributionRecurID) ||
       ($this->_subscriptionDetails == CRM_Core_DAO::$_nullObject)
     ) {
       CRM_Core_Error::fatal('Required information missing.');
@@ -108,12 +117,27 @@ class CRM_Contribute_Form_UpdateSubscription extends CRM_Core_Form {
     }
     $this->assign('self_service', $this->_selfService);
 
-    if (!$this->_paymentProcessorObj->isSupported('changeSubscriptionAmount')) {
-      $userAlert = ts('Updates made using this form will change the recurring contribution information stored in your CiviCRM database, but will NOT be sent to the payment processor. You must enter the same changes using the payment processor web site.');
-      CRM_Core_Session::setStatus($userAlert, ts('Warning'), 'alert');
+    $this->editableScheduleFields = $this->_paymentProcessorObj->getEditableRecurringScheduleFields();
+
+    $changeHelpText = $this->_paymentProcessorObj->getRecurringScheduleUpdateHelpText();
+    if (!in_array('amount', $this->editableScheduleFields)) {
+      // Not sure if this is good behaviour - maintaining this existing behaviour for now.
+      CRM_Core_Session::setStatus($changeHelpText, ts('Warning'), 'alert');
+    }
+    else {
+      $this->assign('changeHelpText', $changeHelpText);
+    }
+    $alreadyHardCodedFields = array('amount', 'installments');
+    foreach ($this->editableScheduleFields as $editableScheduleField) {
+      if (!in_array($editableScheduleField, $alreadyHardCodedFields)) {
+        $this->addField($editableScheduleField, array(
+          'entity' => 'ContributionRecur',
+          'context' => $this->getDefaultContext(),
+        ));
+      }
     }
 
-    $this->assign('isChangeSupported', $this->_paymentProcessorObj->isSupported('changeSubscriptionAmount'));
+    $this->assign('editableScheduleFields', array_diff($this->editableScheduleFields, $alreadyHardCodedFields));
     $this->assign('paymentProcessor', $this->_paymentProcessor);
     $this->assign('frequency_unit', $this->_subscriptionDetails->frequency_unit);
     $this->assign('frequency_interval', $this->_subscriptionDetails->frequency_interval);
@@ -124,7 +148,7 @@ class CRM_Contribute_Form_UpdateSubscription extends CRM_Core_Form {
 
     CRM_Utils_System::setTitle(ts('Update Recurring Contribution'));
 
-    // handle context redirection
+    // Handle context redirection.
     CRM_Contribute_BAO_ContributionRecur::setSubscriptionContext();
   }
 
@@ -134,13 +158,15 @@ class CRM_Contribute_Form_UpdateSubscription extends CRM_Core_Form {
    * Note that in edit/view mode the default values are retrieved from the database.
    */
   public function setDefaultValues() {
-
     $this->_defaults = array();
     $this->_defaults['amount'] = $this->_subscriptionDetails->amount;
     $this->_defaults['installments'] = $this->_subscriptionDetails->installments;
     $this->_defaults['campaign_id'] = $this->_subscriptionDetails->campaign_id;
     $this->_defaults['financial_type_id'] = $this->_subscriptionDetails->financial_type_id;
     $this->_defaults['is_notify'] = 1;
+    foreach ($this->editableScheduleFields as $field) {
+      $this->_defaults[$field] = $this->_subscriptionDetails->$field;
+    }
 
     return $this->_defaults;
   }
@@ -169,7 +195,7 @@ class CRM_Contribute_Form_UpdateSubscription extends CRM_Core_Form {
       CRM_Campaign_BAO_Campaign::addCampaign($this);
     }
 
-    if (CRM_Contribute_BAO_ContributionRecur::supportsFinancialTypeChange($this->_crid)) {
+    if (CRM_Contribute_BAO_ContributionRecur::supportsFinancialTypeChange($this->contributionRecurID)) {
       $this->addEntityRef('financial_type_id', ts('Financial Type'), array('entity' => 'FinancialType'), TRUE);
     }
 
@@ -340,4 +366,11 @@ class CRM_Contribute_Form_UpdateSubscription extends CRM_Core_Form {
     }
   }
 
+  /**
+   * Explicitly declare the form context.
+   */
+  public function getDefaultContext() {
+    return 'create';
+  }
+
 }
index b8792f60c7b3320c95d360fe82a69fce2ebf4709..2a94c7276d20316b12e55f07c7ade5022ad33cd3 100755 (executable)
@@ -418,6 +418,51 @@ abstract class CRM_Core_Payment {
     return $this->_paymentProcessor['payment_type'] == 1 ? $this->getCreditCardFormFields() : $this->getDirectDebitFormFields();
   }
 
+  /**
+   * Get an array of the fields that can be edited on the recurring contribution.
+   *
+   * Some payment processors support editing the amount and other scheduling details of recurring payments, especially
+   * those which use tokens. Others are fixed. This function allows the processor to return an array of the fields that
+   * can be updated from the contribution recur edit screen.
+   *
+   * The fields are likely to be a subset of these
+   *  - 'amount',
+   *  - 'installments',
+   *  - 'frequency_interval',
+   *  - 'frequency_unit',
+   *  - 'cycle_day',
+   *  - 'next_sched_contribution_date',
+   *  - 'end_date',
+   * - 'failure_retry_day',
+   *
+   * The form does not restrict which fields from the contribution_recur table can be added (although if the html_type
+   * metadata is not defined in the xml for the field it will cause an error.
+   *
+   * Open question - would it make sense to return membership_id in this - which is sometimes editable and is on that
+   * form (UpdateSubscription).
+   *
+   * @return array
+   */
+  public function getEditableRecurringScheduleFields() {
+    if (method_exists($this, 'changeSubscriptionAmount')) {
+      return array('amount');
+    }
+  }
+
+  /**
+   * Get the help text to present on the recurring update page.
+   *
+   * This should reflect what can or cannot be edited.
+   *
+   * @return string
+   */
+  public function getRecurringScheduleUpdateHelpText() {
+    if (!in_array('amount', $this->getEditableRecurringScheduleFields())) {
+      return ts('Updates made using this form will change the recurring contribution information stored in your CiviCRM database, but will NOT be sent to the payment processor. You must enter the same changes using the payment processor web site.');
+    }
+    return ts('Use this form to change the amount or number of installments for this recurring contribution. Changes will be automatically sent to the payment processor. You can not change the contribution frequency.');
+  }
+
   /**
    * Get the metadata for all required fields.
    *
index 2eb92a6b3307d9681cd764d74cbe89d71c5e8c7e..645af738ce4632d16e4a1a6afa1979a3e4650a06 100644 (file)
@@ -24,9 +24,9 @@
  +--------------------------------------------------------------------+
 *}
 <div class="crm-block crm-form-block crm-recurcontrib-form-block">
-  {if $isChangeSupported}
+  {if $changeHelpText}
     <div class="help">
-      {ts}Use this form to change the amount or number of installments for this recurring contribution. Changes will be automatically sent to the payment processor. You can not change the contribution frequency.{/ts}
+      {$changeHelpText}
   </div>
   {/if}
   <div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl" location="top"}</div>
     </tr>
     <tr><td class="label">{$form.installments.label}</td><td>{$form.installments.html}<br />
           <span class="description">{ts}Total number of payments to be made. Set this to 0 if this is an open-ended commitment i.e. no set end date.{/ts}</span></td></tr>
+    {foreach from=$editableScheduleFields item='field'}
+      <tr><td class="label">{$form.$field.label}</td><td>{$form.$field.html}</td></tr>
+    {/foreach}
     {if !$self_service}
     <tr><td class="label">{$form.is_notify.label}</td><td>{$form.is_notify.html}</td></tr>
     <tr><td class="label">{$form.campaign_id.label}</td><td>{$form.campaign_id.html}</td></tr>
     <tr><td class="label">{$form.financial_type_id.label}</td><td>{$form.financial_type_id.html}</td></tr>
     {/if}
-
-    {* Currently changes to interval, unit and cycle day are not supported. *}
-    {*
-      <tr><td class="label">{$form.frequency_interval.label}</td><td>{$form.frequency_interval.html}<br />
-          <span class="description">{ts}Number of time units for recurrence of payment.{/ts}</span></td></tr>
-      <tr><td class="label">{$form.frequency_unit.label}</td><td>{$form.frequency_unit.html}<br />
-        <span class="description">{ts}Time unit for recurrence of payment. For example, "month".{/ts}</span></td></tr>
-      <tr><td class="label">{$form.cycle_day.label}</td><td>{$form.cycle_day.html}<br />
-        <span class="description">{ts}Day in the period when the payment should be charged.{/ts}</span></td></tr>
-    *}
   </table>
 
   <div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl" location="bottom"}</div>
index 9e5cfaf6bea222249bc40f90b707f795ed32352c..e1dc131275577b05956942739104bd7737290888 100644 (file)
@@ -39,6 +39,9 @@
     <required>true</required>
     <comment>Amount to be contributed or charged each recurrence.</comment>
     <add>1.6</add>
+    <html>
+      <type>Text</type>
+    </html>
   </field>
   <field>
     <name>currency</name>
@@ -81,6 +84,9 @@
     <required>true</required>
     <comment>Number of time units for recurrence of payment.</comment>
     <add>1.6</add>
+    <html>
+      <type>Text</type>
+    </html>
   </field>
   <field>
     <name>installments</name>
@@ -88,6 +94,9 @@
     <type>int unsigned</type>
     <comment>Total number of payments to be made. Set this to 0 if this is an open-ended commitment i.e. no set end date.</comment>
     <add>1.6</add>
+    <html>
+      <type>Text</type>
+    </html>
   </field>
   <field>
     <name>start_date</name>
     <required>true</required>
     <comment>The date the first scheduled recurring contribution occurs.</comment>
     <add>1.6</add>
+    <html>
+      <type>Select Date</type>
+    </html>
   </field>
   <field>
     <name>create_date</name>
     <required>true</required>
     <comment>When this recurring contribution record was created.</comment>
     <add>1.6</add>
+    <html>
+      <type>Select Date</type>
+    </html>
   </field>
   <field>
     <name>modified_date</name>
     <type>datetime</type>
     <comment>Last updated date for this record. mostly the last time a payment was received</comment>
     <add>1.6</add>
+    <html>
+      <type>Select Date</type>
+    </html>
   </field>
   <field>
     <name>cancel_date</name>
     <type>datetime</type>
     <comment>Date this recurring contribution was cancelled by contributor- if we can get access to it</comment>
     <add>1.6</add>
+    <html>
+      <type>Select Date</type>
+    </html>
   </field>
   <field>
     <name>end_date</name>
     <type>datetime</type>
     <comment>Date this recurring contribution finished successfully</comment>
     <add>1.6</add>
+    <html>
+      <type>Select Date</type>
+    </html>
   </field>
   <field>
     <name>processor_id</name>
     <type>datetime</type>
     <comment>Next scheduled date</comment>
     <add>4.4</add>
+    <html>
+      <type>Select Date</type>
+    </html>
   </field>
   <field>
     <name>failure_count</name>
     <title>Retry Failed Attempt Date</title>
     <comment>Date to retry failed attempt</comment>
     <add>1.6</add>
+    <html>
+      <type>Select Date</type>
+    </html>
   </field>
   <field>
     <name>auto_renew</name>